【发布时间】:2019-12-05 17:35:26
【问题描述】:
多个线程能否在不产生竞争条件的情况下安全地读取同一个类成员变量?
class foo {
int x;
};
void Thread1(foo* bar) {
float j = bar->x * 5;
}
void Thread2(foo* bar) {
float k = bar->x / 5;
}
例如,如果我们有两个线程运行Thread1 和Thread2。如果每个线程都传递了 same foo 对象,它们是否可以独立运行,没有竞争条件,因为我们只读取变量而不写入?还是访问对象的行为使整个事情变得不安全?
如果上述是安全的,只要不触及foo::x,第三个线程是否可以安全地写入同一个foo对象?
#include <thread>
class foo {
public:
int x = 1;
int y = 1;
};
void Thread1(foo* bar) {
int j;
for (int i = 0; i < 1000; i++) {
j = bar->x * 5;
}
printf("T1 - %i\n", j);
}
void Thread2(foo* bar) {
int k;
for (int i = 0; i < 1000; i++) {
k = bar->x / 5;
}
printf("T2 - %i\n", k);
}
void Thread3(foo* bar) {
for (int i = 0; i < 1000; i++) {
bar->y += 3;
}
printf("T3 - %i\n", bar->y);
}
int main() {
foo bar;
std::thread t1(Thread1, &bar);
std::thread t2(Thread2, &bar);
std::thread t3(Thread3, &bar);
t1.join();
t2.join();
t3.join();
printf("x %i, y %i\n", bar.x, bar.y);
return 0;
}
【问题讨论】:
-
是的,我坚信如此。当然,这包括
foo::x不能“间接”写成,例如通过以某种方式完全覆盖foo。 -
您可以从任意数量的线程中读取相同的变量。只有写入需要同步。
-
线程的开始是一个足够的障碍。在线程开始之后(以及在加入之前)对某个地址/范围的读取访问是安全的,并且不需要保护,只要没有其他内容写入该特定地址/范围。
-
在当今大多数处理器中,默认情况下读取对齐的 int 是原子操作,因此它不会造成伤害,但仍然在 C++ 中使用
std::atomic和相应的内存以更明确的方式表达它栅栏参数将不胜感激。 -
@DeanSeo 与对齐的 int 和原子操作有什么关系,如果我唯一要做的就是阅读?即使它是一个大数组,如果根本没有写入,也不会有竞争条件?
标签: c++ thread-safety