有没有办法在Windows上的basic_iostream上获取非锁定流插入/提取?

Ada*_*erg 9 c++ sockets windows string iostream

我是一名C++开发人员,直到最近才开始在Solaris和Linux上编程,当时我不得不创建一个针对Windows的应用程序.

我一直在使用基于TCP套接字支持的C++ I/O流的通信设计.该设计基于从流中连续读取的单个线程(大多数时间在套接字中阻塞等待数据),而其他线程通过相同的流发送(由互斥锁同步).

当移动到Windows时,我选择使用boost :: asio :: ip :: tcp :: iostream来实现套接字流.我很沮丧地发现上面的多线程设计导致了Windows上的死锁.似乎operator<<(std::basic_ostream<...>,std::basic_string<...>)声明了一个'Sentry'来锁定输入和输出操作的整个流.由于我的读线程始终在流上等待,因此在创建此Sentry时,从其他线程发送操作死锁.

以下是operator <<和Sentry构造期间调用堆栈的相关部分:

    ...
    ntdll.dll!7c901046()    
    CAF.exe!_Mtxlock(_RTL_CRITICAL_SECTION * _Mtx=0x00397ad0)  Line 45  C
    CAF.exe!std::_Mutex::_Lock()  Line 24 + 0xb bytes   C++
    CAF.exe!std::basic_streambuf<char,std::char_traits<char> >::_Lock()  Line 174   C++
    CAF.exe!std::basic_ostream<char,std::char_traits<char> >::_Sentry_base::_Sentry_base(std::basic_ostream<char,std::char_traits<char> > & _Ostr={...})  Line 78   C++
    CAF.exe!std::basic_ostream<char,std::char_traits<char> >::sentry::sentry(std::basic_ostream<char,std::char_traits<char> > & _Ostr={...})  Line 95 + 0x4e bytes  C++
>   CAF.exe!std::operator<<<char,std::char_traits<char>,std::allocator<char> >(std::basic_ostream<char,std::char_traits<char> > & _Ostr={...}, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & _Str="###")  Line 549 + 0xc bytes   C++
    ...
Run Code Online (Sandbox Code Playgroud)

如果istream和ostream组件被单独锁定,我会没事的,但事实并非如此.

我可以使用流操作符的替代实现吗?我可以指示它不要锁吗?我应该实现自己的(不知道如何做到这一点)?

任何建议,将不胜感激.

(平台是Windows 32位和64位.使用Visual Studio 2003 Pro和2008 Express观察到的行为)

Ada*_*erg 1

这个问题已经被搁置了很长时间了。我将报告我最终所做的事情,即使我有可能会被嘲笑。

我已经确定问题是两个线程在尝试在单独的读写操作中访问 iostream 对象时陷入死锁。我可以看到字符串流插入和提取运算符的 Visual Studio 实现都声明了 Sentry,它锁定了与正在操作的流关联的流缓冲区。

我知道,对于此死锁所涉及的流,流缓冲区实现是 boost::asio::basic_socket_streambuf。我检查了实现,发现读取和写入操作(下溢和溢出)实际上在不同的缓冲区上运行(获取与放置)。

经过上述验证,我选择简单地规避此应用程序的锁定。为此,我使用特定于项目的预处理器定义来排除锁定哨兵的 basic_istream 实现中的锁定代码:

    class _Sentry_base
        {   // stores thread lock and reference to input stream
    public:
        __CLR_OR_THIS_CALL _Sentry_base(_Myt& _Istr)
            : _Myistr(_Istr)
            {   // lock the stream buffer, if there
#ifndef MY_PROJECT
            if (_Myistr.rdbuf() != 0)
                _Myistr.rdbuf()->_Lock();
#endif
            }

        __CLR_OR_THIS_CALL ~_Sentry_base()
            {   // destroy after unlocking
#ifndef MY_PROJECT
            if (_Myistr.rdbuf() != 0)
                _Myistr.rdbuf()->_Unlock();
#endif
            }
Run Code Online (Sandbox Code Playgroud)

优点:

  • 有用
  • 只有我的项目(具有适当的定义)受到影响

缺点:

  • 感觉有点哈克
  • 构建此平台的每个平台都需要进行此修改

我计划通过在代码和项目文档中大声记录这一点来缓解后一点。

我意识到可能有一个更优雅的解决方案,但为了权宜之计,我在尽职调查以了解影响后选择了直接解决方案。