【发布时间】:2021-07-12 13:56:36
【问题描述】:
TL;DR:说特定函数是“线程安全”的,因为同时调用两个可能不同的函数会发生数据竞争/em> 函数?这个问题在人们告诉“const 意味着/暗示 C++11 中的线程安全”[1][2]
考虑以下示例:
class X {
int x, y; // are some more complex type (not supported by `std::atomic`)
std::mutex m;
public:
void set_x (int new_x) {x = new_x;} // no mutex
void get_x () const {return x;}
void set_y (int new_y) {
std::lock_guard<std::mutex> guard(m); // guard setter with mutex
y = new_y;
}
void get_y () const {return y;}
}
set_x 线程安全吗?
当然,set_x 不是线程安全的,因为同时从两个线程调用它会导致数据争用。
get_x、get_y 和 set_y 线程安全吗?
存在两种可能的推理:
- 是的,它们是线程安全的,因为同时从两个线程调用
get_x/get_y/set_y不会导致数据争用。 - 不,它们不是线程安全的,因为从两个线程同时调用
get_x(或get_y)和set_x(或set_y)会导致数据争用。
这三个函数的正确推理是哪一个?
问题总结
哪种推理是正确的?
- 一个函数是线程安全的,如果同时从两个线程调用它不会导致数据竞争。可以为
set_x/get_x工作,但对set_y/get_y无效,因为这将得出结论set_y和get_y是线程安全的,但类Y不是同时从两个线程调用set_y和get_y会导致数据争用。 - 一个函数是线程安全的,当且仅当它不访问任何可以在没有另一个函数内部同步的情况下修改的内存时。在我看来,这似乎是最一致的选项,但不是经常使用的方式(参见相关线程)。
相关话题
请注意,我已阅读以下相关主题:
【问题讨论】:
-
考虑使用
std::atomic<int> x;而不是int x;来实现线程安全。 -
@RichardCritten,你是绝对正确的,但这不足以确保线程安全。用于操作共享变量 x 的方法必须采用同步方式。
-
@ManifestMan 对于
std::atomic<T>::load和std::atomic<T>::store两种方法,默认的内存排序值为std::memory_order_seq_cst。无需在代码中添加不必要的混乱。在深处,std::atomic通过在两个不同级别上表现得像栅栏/屏障来实现其目的:在编译器级别,内存操作不会跨屏障重新排序;在机器指令级别,发出必要的架构内存屏障指令,具体取决于 CPU 架构和调用的std::atomic方法。 (续...) -
@ManifestMan (...cont'd) 请注意,
std::atomic的设计似乎有助于将其用作信号量;也就是说,在一些其他数据已完全写入(由该writer-core)之后由一个核心设置的标志变量;以及一个标志变量,在开始对其他数据的读取操作之前,其他内核将检查该变量。就 x86 和 ARM 而言,std::atomic的当前 C++ 库实现足以保证正确操作,即使 那些其他数据 不是使用特殊方法写入或读取的。 (续...)
标签: c++ multithreading c++11 thread-safety