【发布时间】:2018-08-08 15:10:16
【问题描述】:
可以依赖外部 I/O 作为跨线程同步的一种形式吗?
具体来说,考虑下面的伪代码,它假设存在网络/套接字函数:
int a; // Globally accessible data.
socket s1, s2; // Platform-specific.
int main() {
// Set up + connect two sockets to (the same) remote machine.
s1 = ...;
s2 = ...;
std::thread t1{thread1}, t2{thread2};
t1.join();
t2.join();
}
void thread1() {
a = 42;
send(s1, "foo");
}
void thread2() {
recv(s2); // Blocking receive (error handling omitted).
f(a); // Use a, should be 42.
}
我们假设远程机器仅在收到来自s1 的"foo" 时才向s2 发送数据。如果这个假设失败,那么肯定会导致未定义的行为。但如果它成立(并且没有发生其他外部故障,如网络数据损坏等),这个程序是否会产生定义的行为?
“从不”、“未指定(取决于实现)”、“取决于发送/接收实现提供的保证”是我期望的那种示例答案,最好有 C++ 标准(或其他相关标准,例如用于套接字/网络的 POSIX)。
如果“从不”,则将 a 更改为初始化为确定值(例如 0)的 std::atomic<int> 将避免未定义的行为,但该值保证在 thread2 中被读取为 42,或者可以读取过时的值? POSIX 套接字是否提供进一步的保证以确保不会读取过时的值?
如果“依赖”,POSIX 套接字是否提供相关保证以使其定义行为? (如果s1 和s2 是同一个套接字而不是两个独立的套接字呢?)
作为参考,标准 I/O 库有一个条款似乎在使用 iostreams 时提供了类似的保证(N4604 中的 27.2.3¶2):
如果一个线程调用库 a 将值写入流,结果,另一个线程通过库调用 b 从流中读取该值,这样不会导致数据争用,那么 a写与 b 的读同步。
那么底层网络库/函数是否提供了类似的保证?
实际上,对于send 和recv 函数,编译器似乎无法重新排序对全局a 的访问(因为它们原则上可以使用a)。但是,运行thread2 的线程仍然可以读取a 的陈旧值,除非send/recv 对本身提供某种内存屏障/同步保证。
【问题讨论】:
-
如果有人知道 C++ TS Extensions for Networking (open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4711.pdf) 是否解决了这一点也会很有趣。第一次浏览时看不到任何相关的内容。
标签: c++ multithreading sockets network-programming memory-model