C++ istream和ostream具有共享的streambuf,对于双工I/O是线程安全的?
我已经为缓冲的网络套接字I/O派生了一个自定义streambuf,它覆盖了下溢、溢出和同步,这样下溢和其他两个的集合是线程安全的(我有单独的输入和输出内部缓冲区)。这很好,但我想将其用于全双工I/O,其中一个线程可以输入,而另一个线程可以输出,因此我想对接收线程使用istream,对发送线程使用ostream,同时共享网络流BUF,因为它抽象了所有套接字内容。我的问题是,如果输入和输出缓冲区是分开的,那么istream上的输入操作对streambuf成员的影响程度与ostream上的输出操作对streambuf成员的影响程度如何C++ istream和ostream具有共享的streambuf,对于双工I/O是线程安全的?,c++,multithreading,sockets,stream,iostream,C++,Multithreading,Sockets,Stream,Iostream,我已经为缓冲的网络套接字I/O派生了一个自定义streambuf,它覆盖了下溢、溢出和同步,这样下溢和其他两个的集合是线程安全的(我有单独的输入和输出内部缓冲区)。这很好,但我想将其用于全双工I/O,其中一个线程可以输入,而另一个线程可以输出,因此我想对接收线程使用istream,对发送线程使用ostream,同时共享网络流BUF,因为它抽象了所有套接字内容。我的问题是,如果输入和输出缓冲区是分开的,那么istream上的输入操作对streambuf成员的影响程度与ostream上的输出操作对s
更好的做法是能够做到这一点,而不是必须将套接字内容从我的streambuf抽象中分离出来,这样就可以使用单独的streambuf在istream和ostream之间共享套接字——然后我还需要两个版本的streambuf——一个具有单个内部缓冲区(仅用于istream或ostream),还有一个像我现在一样有两个内部缓冲区,用于iostream。。。很糟糕,因为这是额外的类和代码复制。输入和输出序列基本上是独立的。下面有一个很好的图表: 输入和输出序列之间唯一共享的是
locale
对象,该对象包含用于执行文本编码转换的codevt
方面
理论上,中途更改文本编码是线程不安全的,但实际上库根本不支持该操作
你应该可以走了。没有为
std::streambuf
(或std::basic_streambuf
)提供比一般情况下更多的保证。也就是说,您可以让多个线程在任何时候读取对象的状态,但是如果有一个线程修改对象的状态,那么就不会有其他线程访问对象。读取和写入字符都会修改流缓冲区的状态,即从形式上看,如果没有外部同步,就不能使用它们
在内部,这两个缓冲区是完全分开的,彼此没有任何关系。流缓冲区上的操作以一种相当结构化的方式修改它们,我无法想象任何实现都会在两组指针之间有显式的交互。也就是说,在实际应用中,我认为阅读和写作之间不需要任何同步。但是,我以前没有意识到这两组缓冲区指针实际上可能共享相同的缓存线,这至少会导致性能问题。我不认为这会导致任何正确性问题
两个流缓冲区之间可能共享的唯一资源是std::locale
对象,但是该对象是无状态的。另外,std::streambuf
不会使用此对象本身:它是您的流缓冲区,可能会使用某些方面(例如std::codecvt
方面)。当通过调用虚拟函数imbue()
更改区域设置时,如果流缓冲区使用该区域设置,您将能够拦截此更改并执行所需的任何同步
总之,该标准不能保证使用同一个流缓冲区使用并发线程进行读写。实际上,DS9k可能是唯一一个出现故障的系统,由于缓冲区指针位于共享缓存线中,两个线程可能最终有效地同步。对于全双工,您需要两个缓冲区。如果对这两种接口都使用streambuf接口,以便将m连接到常用的ostream和istream接口,则整个画面如下所示: 这两个缓冲区显然是完全独立和对称的,因此我们可以忽略一侧,只关注单个缓冲区 此外,可以安全地假设只有两个线程:读取线程和写入线程。如果涉及更多线程,那么两个线程将同时读取或写入;这将导致不良的种族状况,因此毫无意义。我们可以假设用户将有某种机制,确保一次只有一个线程写入streambuf,同样,一次只有一个线程读取streambuf 在最一般的情况下,实际缓冲区存在多个连续内存块。每个put-and-get区域都完全位于一个这样的块中。只要它们在不同的内存块中,那么它们也是不相关的 每个get/put区域都有三个指针:一个指针指向区域的开始(eback/pbase),一个指针指向区域结束后的一个字节(egptr/epptr),一个指针指向区域中的当前位置(gptr/pptr)。从
std::streambuf
派生的类可以通过同名受保护的访问器(eback()
,pbase()
,egptr()
,epptr()
,gptr()
和pptr()
)直接访问这些指针注意,这里我们指的是一个streambuf的eback()、egptr()和gptr()
,另一个streambuf的pbase()、epptr()和pptr()
(见上图)
std::streambuf
具有访问或更改这六个指针的公共函数。它们是:
表,th,td{
边框:1px纯黑;
边界塌陷:塌陷;
}
th,td{
填充物:5px;
}
std::streambuf的公共成员函数
方法更改和/或访问
pubsetbuf()
调用派生最多的类的setbuf()
pubseek