带有共享流缓冲的 istream 和 ostream 对双工 I/O 是线程安全的吗?
我已经为缓冲网络套接字 I/O 派生了一个自定义流缓冲,覆盖下溢、溢出和同步,以便下溢与其他两个的集合是相互线程安全的,(我有单独的输入和输出内部缓冲区).这很好用,但我想将它用于全双工 I/O,其中一个线程可以输入,而另一个线程正在输出,所以我想使用 istream 作为接收线程,使用 ostream 作为发送线程,同时共享网络streambuf 因为它抽象了所有套接字的东西.我的问题是,如果输入和输出缓冲区是分开的,那么受 istream 上的输入操作影响的流缓冲成员在多大程度上与受 ostream 上的输出操作影响的流缓冲成员不相交?
I've derived a custom streambuf for buffered network socket I/O, overriding underflow, overflow, and sync so that underflow is mutually thread-safe with the set of the other two, (I have separate input and output internal buffers). That works fine, but I want to use this for full duplex I/O where one thread can input while another is outputting, so I'd like to use an istream for the receiving thread and ostream for the sending one, while sharing the network streambuf as that abstracts all the socket stuff. My question is, to what extent are the streambuf members affected by input operations on an istream disjoint from the streambuf members affected by output operations on an ostream if the input and output buffers are separate?
能够做到这一点会更好,而不是必须将套接字内容与我的流缓冲抽象分离,以便可以使用单独的流缓冲在 istream 和 ostream 之间共享套接字――那么我还需要两个版本的streambuf--一个带有单个内部缓冲区(仅用于 istream 或仅用于 ostream),一个带有两个内部缓冲区,就像我现在拥有的,用于 iostream ......很糟糕,因为这是额外的类和代码重复.>
It would be better to be able to do that rather than have to separate the socket stuff from my streambuf abstraction so the socket can be shared between istream and ostream with separate streambufs--then I'd also need two versions of the streambuf--one with a single internal buffer (for use in either istream only or ostream only), and one with two internal buffers as I have now, for use in iostream... sucks as that's additional classes and code duplication.
推荐答案
std::streambuf
(或 std::basic_streambuf<...>
) 提供了比通常提供的更多的保证.也就是说,您可以随时让多个线程读取对象的状态,但如果有一个线程修改对象的状态,则不应有其他线程访问该对象.读取和写入字符都会修改流缓冲区的状态,即从正式的角度来看,您不能在没有外部同步的情况下使用它们.
There is no special guarantee given for std::streambuf
(or std::basic_streambuf<...>
) which gives more guarantees than what is generally given. That is, you can have multiple threads reading the object's state at any time but if there is one thread modifying the object's state there shall be no other thread accessing the object. Both reading and writing characters modify the stream buffer's state, i.e. from a formal point of view you can't use them without external synchronization.
在内部,这两个缓冲区是完全独立的,彼此没有任何关系.流缓冲区上的操作以一种相当结构化的方式修改它们,我无法想象任何实现都会在两组指针之间进行显式交互.也就是说,实际上,我认为阅读和写作之间不需要任何同步.但是,我之前没有意识到这两组缓冲区指针实际上可能共享相同的缓存行,这至少可能会导致性能问题.我认为这不会导致任何正确性问题.
Internally the two buffers are entirely separate and have nothing to do with each other. The operations on stream buffers modify them in a rather structured way and I can't imagine that any implementation would have an explicit interaction between the two sets of pointers. That is, in practical terms I don't think there is any synchronization necessary between reading and writing. However, I hadn't realized before that the two sets of buffer pointers may actually share the same cache lines which may at least cause performance problems. I don't think this should cause any correctness problems.
可能在两个流缓冲区之间共享的唯一资源是 std::locale
对象,但是它是无状态的.此外,std::streambuf
本身不使用此对象:它是您的流缓冲区,可能会使用某些方面(例如 std::codecvt<...>
方面).由于通过调用虚函数 imbue()
来更改区域设置,如果您的流缓冲区使用该区域设置,您将能够拦截此更改并执行所需的任何同步.
The only resource possibly shared between the two stream buffers is the std::locale
object which is meant to be stateless, however. Also, std::streambuf
doesn't make any use of this object itself: it is your stream buffer which may use some of the facets (e.g. the std::codecvt<...>
facet). As the locale is changed via a call to the virtual function imbue()
you would be able to intercept this change and do whatever synchronization is needed if your stream buffer uses the locale.
总而言之,该标准不保证可以使用并发线程使用相同的流缓冲区进行读取和写入.实际上,DS9k 可能是唯一会失败的系统,并且由于缓冲区指针最终位于共享缓存行中,因此两个线程可能最终有效同步.
In summary, the standard doesn't make any guarantee that it would work to use concurrent threads to read and write using the same stream buffer. In practice, the DS9k is probably the only system where it would fail and the two threads may end up effectively synchronized due to the buffer pointers ending up in shared cache lines.
相关文章