快速提问?这个行在C ++和Java中是原子的吗?
class foo {
bool test() {
// Is this line atomic?
return a==1 ? 1 : 0;
}
int a;
}
如果有多个线程访问该行,我们最终可能会进行检查 a == 1首先,然后a更新,然后返回,对吗?
补充:我没有完成课程,当然还有其他部分更新了......
答案 0 :(得分:8)
不,对于C ++和Java。
在Java中,您需要以同样的方式制作方法synchronized
并保护a
的其他用途。确保在所有情况下都在同一个对象上进行同步。
在C ++中,您需要使用std::mutex
来保护a
,可能使用std::lock_guard
来确保在函数结束时正确解锁互斥锁。
答案 1 :(得分:7)
return a==1 ? 1 : 0;
是一种简单的写作方式
if(a == 1)
return 1;
else
return 0;
我没有看到任何更新的代码。但我认为你可以搞清楚。
答案 2 :(得分:5)
无论是否存在写入,在C ++中读取非原子类型的值都是不原子操作。如果没有写入,那么你可能不在乎它是否是原子的;如果某个其他线程可能正在修改该值,那么你肯定会关心。
答案 3 :(得分:2)
正确的放置方式很简单:不! (适用于Java和C ++)
一个不太正确但更实际的答案是:从技术上讲,这不是原子的,但在大多数主流架构中,至少对于C ++来说。
您发布的代码中没有修改任何内容,仅测试变量。因此,代码通常会导致访问该存储器位置的单个TEST
(或类似)指令,即,本质上是原子的。该指令将读取一个缓存行,并且在相应的loaction中将有一个明确定义的值,无论它是什么。
然而,这是一个inc inc / acc,,不是你可以依赖的东西。
当一个其他线程写入该值时,它通常会再次,无意地/意外地工作。为此,CPU获取高速缓存行,覆盖高速缓存行中相应地址的位置,并将整个高速缓存行写回RAM。测试变量时,将获取包含旧值或新值的缓存行(两者之间没有任何内容)。没有发生 - 在任何形式的保证之前,但你仍然可以认为这是“原子”。
当多个线程同时修改该变量时(这不是问题的一部分),这要复杂得多。为了使其正常工作,您需要使用C ++ 11 <atomic>
中的某些内容,或使用原子内在函数或类似内容。否则,很不清楚会发生什么,以及操作的结果可能是什么 - 一个线程可能读取该值,递增并将其写回,但另一个线程可能会在写入修改后的值之前读取原始值。
在所有当前平台上,这或多或少地保证会严重结束。
答案 4 :(得分:1)
不,它不是原子的(一般情况下),虽然它可以在某些体系结构中(例如,在C ++中,如果整数是对齐的,那么除非你强制它不是这样)。
考虑这三个主题:
// thread one: // thread two: //thread three
while (true) while (true) while (a) ;
a = 0xFFFF0000; a = 0x0000FFFF;
如果对a
的写入不是原子的(例如,如果a
未对齐则为intel,并且为了在两个连续高速缓存行中的每一个中以16位进行讨论)。现在看来第三个线程似乎不能从循环中出来(a
的两个可能值都是非零),事实是赋值不是原子的,线程2可以更新更高的16位在线程2获得完成更新的时间之前,线程3可以将低16位读取为0,然后退出循环。
整个条件与问题无关,因为返回的值是线程的本地值。
答案 5 :(得分:0)
不,它仍然是一个测试,然后是一个集合,然后是一个返回。
是的,多线程将是一个问题。
这只是语法糖。
答案 6 :(得分:0)
您的问题可以改为:声明:
a == 1
原子与否?不,它不是原子的,你应该使用std :: atomic来检查某种情况下的条件。如果整个三元运算符原子与否在这种情况下无关紧要,因为它不会改变任何东西。如果你的问题是在这个代码中的意思:
bool flag = somefoo.test();
标志与== 1一致,它肯定不会,如果你的问题中的整个三元运算符都是原子的,则无关紧要。
答案 7 :(得分:0)
这里有很多好的答案,但没有一个人提到Java需要将a
标记为volatile
。
如果没有采用其他同步方法,则尤其很重要,但其他线程可以更新a
。否则,您可能正在阅读旧值a
。
答案 8 :(得分:0)
请考虑以下代码:
bool done = false;
void Thread1() {
while (!done) {
do_something_useful_in_a_loop_1();
}
do_thread1_cleanup();
}
void Thread2() {
do_something_useful_2();
done = true;
do_thread2_cleanup();
}
这两个线程之间的同步是使用布尔变量done完成的。这是同步两个线程的错误方法。
在x86上,最大的问题是编译时优化。
do_something_useful_2()的部分代码可以由编译器移动到“done = true”以下。 do_thread2_cleanup()的部分代码可以由编译器移到“done = true”之上。 如果do_something_useful_in_a_loop_1()没有修改“done”,编译器可能会以下列方式重写Thread1:
if (!done) {
while(true) {
do_something_useful_in_a_loop_1();
}
}
do_thread1_cleanup();
所以Thread1永远不会退出。
在x86以外的体系结构上,缓存效果或无序指令执行可能会导致其他细微问题。
大多数比赛探测器都会发现这样的种族。
此外,大多数动态竞赛探测器将报告与此bool同步的内存访问数据竞赛
(即在do_something_useful_2()和do_thread1_cleanup()之间)
要修复此类竞赛,您需要使用编译器和/或内存障碍(如果您不是专家 - 只需使用锁定。)