我是一名 C++ 开发人员,主要在 Solaris 和 Linux 上进行编程,直到最近我被迫创建一个针对 Windows 的应用程序。
我一直在使用基于 TCP 套接字支持的 C++ I/O 流的通信设计。该设计基于单个线程从流中连续读取(大部分时间阻塞在套接字读取等待数据),而其他线程通过同一流发送(由互斥锁同步)。
迁移到 Windows 时,我选择使用 boost::asio::ip::tcp::iostream 来实现套接字流。我很沮丧地发现上面的多线程设计导致了 Windows 上的死锁。似乎operator<<(std::basic_ostream<...>,std::basic_string<...>)
声明了一个“哨兵”,它为输入和输出操作锁定了整个流。由于我的读取线程始终在流上等待,因此在创建此 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++
...
如果 istream 和 ostream 组件被分别锁定,我会很好,但事实并非如此。
我可以使用流运算符的替代实现吗?我可以指示它不锁定吗?我应该实现自己的(不知道该怎么做)?
任何建议,将不胜感激。
(平台是 Windows 32 位和 64 位。使用 Visual Studio 2003 Pro 和 2008 Express 观察到的行为)