考虑这个例子:
#include <iostream>
#include <atomic>
void close(int);
void use(int);
int create();
int main(){
std::atomic<bool> is_shutdown = false;
int fd = create();
std::thread t1([&](){
is_shutdown.store(true);
close(fd); // #1
});
std::thread t2([&]{
if(is_shutdown.load()){ // #2
return;
}
use(fd); // #3
});
t1.join();
t2.join();
}
假设
use(fd)
在 fd
被 close
关闭后会出现异常。在 #2
被 false
关闭后,fd
可能仍然读取 #1
。所以,快速检查并不可靠。是不是意味着这两个操作必须串行化?换句话说,它们必须被放置在关键部分,以便它们被专门执行。
是的。您需要序列化这些操作。即使您使用 std::atomic 进行 is_shutdown,仍然存在竞争条件。
这就是可能发生的事情:
线程 t2 检查 is_shutdown 并发现它是 false (#2)。
在 t2 调用 use(fd) 之前,线程 t1 将 is_shutdown 设置为 true 并关闭 fd (#1)。
现在 t2 在关闭的 fd 上调用 use(fd) (#3)。
这意味着 t2 关闭后可能会使用 fd,这可能会导致错误。检查 (is_shutdown.load()) 和操作 (use(fd)) 不会同时发生。
您应该使用互斥体或某种同步来确保当一个线程使用 fd 时,另一个线程不会关闭它。这样,要么线程看到关闭已经发生并且不使用 fd,要么它安全地使用 fd 而不必担心它被关闭。