在此示例代码中,我执行从i=0
到i=n
的求和,然后将结果添加到自身k
次,其中k
是线程数。我故意在没有critical
(围绕printf
和ans += ans
)的情况下做到这一点,导致竞争条件。然而,令我惊讶的是,没有发生任何竞争条件:
int summation_with_operation_after_it_wrong1(int n, int k) {
int ans = 0;
#pragma omp parallel firstprivate(n) num_threads(k)
{
int i; /* Private */
#pragma omp for schedule(dynamic) reduction(+:ans)
for (i = 0; i < n; i++) {
ans += i;
}
printf("Thread %d ans=%d\n", omp_get_thread_num(), ans);
ans += ans;
}
return ans;
}
使用n=10
和k=4
,输出(总是相同,除了线程顺序):
Thread 1 ans=45
Thread 3 ans=45
Thread 0 ans=45
Thread 2 ans=45
720
但是,我确实发现了一些奇怪的事情。 ans
总是45,而不是
Thread 3 ans=45
Thread 0 ans=90
Thread 2 ans=180
Thread 1 ans=360
720
使用critical
时。所以我将printf
移到了ans += ans
之后,看看它在做什么,而且,令我惊讶的是,预测的竞争条件一直开始发生!
Thread 3 ans=90
Thread 1 ans=135
Thread 2 ans=90
Thread 0 ans=135
135
那么...... printf
如何阻止竞争条件?那个数额最终是如何达到720?我完全迷失在这里。
答案 0 :(得分:2)
最新OpenMP standard的第1.4节规定了竞争条件的结果(强调我的):
如果多个线程在没有同步的情况下写入同一个内存 单位,包括由于所述的原子性考虑而引起的情况 在上面,然后发生数据竞争。同样,如果至少有一个线程 从内存单元读取,至少一个线程写入没有 同步到同一个内存单元,包括由于的情况 如上所述的原子性考虑,然后发生数据竞争。 如果发生数据争用,则无法指定程序的结果。
您注意到的内容与粗体语句完全一致。事实上,由于未指定包含数据争用的程序中的行为,因此争论为什么特定输出来自给定运行是没有意义的。特别是,只有在720
命令之前插入printf
时才获得ans+=ans
,并且无法保证您始终会遇到相同的行为。
答案 1 :(得分:1)
printf()
是一个非常昂贵的号召,使用它会改变你的竞争条件时机并不奇怪。查看发生了什么的更好选择是(预先)创建一个数组来存储结果,并让每个线程将其结果存入此阵列,您当前正在执行打印;在完成所有工作后,再做实际的printf()
。