有没有办法在Windows中的basic_iostream上插入/提取非锁定流? 我是一个C++开发人员,在最近我被迫创建一个面向Windows的应用程序时,主要是在Solaris和Linux上编程。 我一直在使用基于TCP套接字支持的C++ I/O流的通信设计。该设计基于单个线程连续读取流(大部分时间在套接字读取等待数据时阻塞),而其他线程通过同一流发送(通过互斥同步)
当移动到windows时,我选择使用boost::asio::ip::tcp::iostream来实现套接字流。我沮丧地发现,上面的多线程设计导致了Windows上的死锁。看来有没有办法在Windows中的basic_iostream上插入/提取非锁定流? 我是一个C++开发人员,在最近我被迫创建一个面向Windows的应用程序时,主要是在Solaris和Linux上编程。 我一直在使用基于TCP套接字支持的C++ I/O流的通信设计。该设计基于单个线程连续读取流(大部分时间在套接字读取等待数据时阻塞),而其他线程通过同一流发送(通过互斥同步),c++,windows,string,sockets,iostream,C++,Windows,String,Sockets,Iostream,当移动到windows时,我选择使用boost::asio::ip::tcp::iostream来实现套接字流。我沮丧地发现,上面的多线程设计导致了Windows上的死锁。看来操作符也许您可以自己实现一个锁定层?也就是说,有一个单独的istream和ostream,当调用它们时,您自己将其锁定。定期检查两个流是否都已解锁,然后从一个流读取到另一个流。写入流后是否显式刷新流?这意味着您的数据可能只是“卡在”缓冲区中。如果这是真的,那么您可能会出现死锁,因为还没有可供阅读的内容。Addstream根
操作符也许您可以自己实现一个锁定层?也就是说,有一个单独的istream
和ostream
,当调用它们时,您自己将其锁定。定期检查两个流是否都已解锁,然后从一个流读取到另一个流。写入流后是否显式刷新流?这意味着您的数据可能只是“卡在”缓冲区中。如果这是真的,那么您可能会出现死锁,因为还没有可供阅读的内容。Addstream根据boost文档[1],使用两个线程访问一个对象而不使用互斥锁是“不安全的”。仅仅因为它在Unix平台上工作并不能保证它在Windows平台上工作
因此,您的选择是:
重写代码,使线程不会同时访问对象
修补boost库并将更改发送回
问Chris他是否会对Windows平台进行更改
[1] 这个问题已经拖得够久了。我要报告我最后做了什么,即使有可能我会被嘲笑
我已经确定问题在于两个线程在分别执行读取和写入操作时试图访问iostream对象时出现死锁。我可以看到,字符串流插入和提取操作符的VisualStudio实现都声明了一个Sentry,它锁定了与正在操作的流相关联的流缓冲区
我知道,对于这个死锁所涉及的流,流缓冲区实现是boost::asio::basic_socket_streambuf。我检查了实现,以查看读写操作(下溢和溢出)实际上在不同的缓冲区上运行(get与put)
通过以上验证,我选择了简单地绕过此应用程序的锁定。为此,我使用特定于项目的预处理器定义来排除locking sentry的基本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
}
上涨:
- 它起作用了
- 只有我的项目(具有适当的定义)受到影响
缺点:
- 感觉有点不舒服
- 建造此平台的每个平台都需要进行此修改
我计划通过在代码和项目文档中大声记录这一点来缓解后一点
我意识到可能有一个更优雅的解决方案,但为了权宜之计,我在尽职调查后选择了一个直接解决方案,以了解其影响。我知道这是一个老问题。。。但我必须自己做这件事
我的情况更复杂,因为这是我自己的streambuf,但您可以通过以下方式解决此问题:
std::ostream &operator<<(std::ostream &x, std::string &y)
{
x.rdbuf()->_Unlock();
x << y.c_str();
}
std::ostream&operator+1个好问题,措辞得体。可惜我没有答案给你!我实际上在消息边界处刷新流,但这不是问题所在。此连接的另一端未在等待数据时卡住。相反,两个线程在windows流定义的iostream“sentry”上处于死锁状态。谢谢你的考虑。谢谢你的意见。只是想澄清一下——这个问题特别涉及到basic_ostream模板的Visual Studio实现,它扩展了一个名为Sentry的锁定和错误检查类。这不是boost::asio库的问题(尽管您的引用值得注意)。是否需要将tcp::iostream转换为Visual Studio的std::iostream?如果你一直保持它的tcp::iostream,你可能会有更多的运气。我一路上都没想过。。。我通常用最小公分母(istream/ostream)写我的类的输入/输出(读/写等)。谢谢你的思考。刚刚看到这篇文章,它链接到另一个问题:
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
}
std::ostream &operator<<(std::ostream &x, std::string &y)
{
x.rdbuf()->_Unlock();
x << y.c_str();
}