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观察到的行为)
这个问题已经被搁置了很长时间了。我将报告我最终所做的事情,即使我有可能会被嘲笑。
我已经确定问题是两个线程在尝试在单独的读写操作中访问 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)
优点:
缺点:
我计划通过在代码和项目文档中大声记录这一点来缓解后一点。
我意识到可能有一个更优雅的解决方案,但为了权宜之计,我在尽职调查以了解影响后选择了直接解决方案。