所以我有一小段代码:
int a = 10;
bool finished = false;
#pragma omp parallel num_threads(3) shared(a, finished)
{
while(!finished) {
#pragma omp single nowait
{
printf("[%d] a is: %d\n", omp_get_thread_num(), a);
a--;
finished = true;
}
}
}
输出
[0] a is: 10
[2] a is: 10
[1] a is: 10
这根本不是我期望的。我意识到所有的线程可能会在退出while循环之前进入单个构造中,但是为什么它们都说同样的?输入的第二个线程应为a = 9,第三个线程a = 8。 我尝试过#pragma omp flush和#pragma omp atomic但没有用。我希望在该块中使用a进行比较(即,如果(a == 10)),那么一旦另一个线程进入单个块就更新该值是至关重要的。我做错了什么?
答案 0 :(得分:1)
您的代码存在的问题是single
指令基本上没有效果。通过指定nowait
子句,其他线程不会在块结束时等待,而是立即进入下一个循环迭代。
这意味着,到达single
构造的第一个线程将在第一次迭代中执行该块。剩下的线程将跳过该块并在第二次迭代中立即再次到达single
构造。然后剩下的两个线程中的一个进入块(因为它是一个新的迭代,因此块的另一个出现)。另一个在第三次迭代中再次跳过并立即进入。最后,所有线程几乎同时执行并打印初始值a
,因为在此时间点之前没有任何线程成功递减a
。
如果您在a
块内交换语句,您将看到single
更改,如下所示。但是,价值及其顺序将不确定。
#pragma omp single nowait
{
a--;
printf("[%d] a is: %d\n", omp_get_thread_num(), a);
finished = true;
}
答案 1 :(得分:0)
我不相信你想要一个执行你的代码块的线程,而是你想要确保每个线程都不会踩到其他线程' a
和finished
中的数据。这是通过#pragma omp critical
完成的。此外,您不能拥有nowait
子句,因为根据定义,在其他线程完成关键区域之前,其他线程不会进入代码块。
while(!finished) {
// only 1 thread will enter this region at a time
#pragma omp critical
{
printf("[%d] a is: %d\n", omp_get_thread_num(), a);
a--;
finished = true;
}
}
另外,请注意,这种线程相互依赖可能会破坏性能。我建议你避免这种情况并改变你的算法,除非你没有别的办法去做。
答案 2 :(得分:0)
我真的不确定你在这里尝试了什么......实际上,在single
区块以外的其他线程没有完成任何工作的情况下,我看不出结构的重点
无论如何,我尝试通过在块外部添加printf()
语句来推断一点并扩展您的示例,该语句还打印a
的值以查看如何将其传输到其他线程。此外,由于您使用了single
指令,我假设您只希望一个线程执行该块,即使它位于while()
循环上。所以它看起来非常适合使用OpenMP锁......
以下是我提出的建议:
#include <stdio.h>
#include <omp.h>
#include <unistd.h>
int main() {
int a = 10;
bool finished = false;
omp_lock_t lock;
omp_init_lock( &lock );
#pragma omp parallel num_threads( 3 ) shared( a, finished )
{
while( !finished ) {
if ( omp_test_lock( &lock ) ) {
printf( "[%d] a is: %d\n", omp_get_thread_num(), a );
#pragma omp atomic update
a--;
usleep( 10 );
finished = true;
#pragma omp flush( finished )
omp_unset_lock( &lock );
}
#pragma omp flush( finished, a )
printf( "[%d] outside of if block, a is: %d\n", omp_get_thread_num(), a );
}
}
return 0;
}
我已添加对usleep()
的调用,以延迟执行if
块内的指令,并为其他线程提供打印内容的机会。我已经使用gcc版本4.9和5.3以及英特尔编译器16.0对它进行了测试,并且所有3个都给了我相同类型的输出,(显然在运行之间的顺序和打印数量有一些变化)。
结果如下:
~/tmp$ icpc -fopenmp omplock.cc
~/tmp$ ./a.out
[0] a is: 10
[1] outside of if block, a is: 10
[1] outside of if block, a is: 9
[1] outside of if block, a is: 9
[1] outside of if block, a is: 9
[1] outside of if block, a is: 9
[1] outside of if block, a is: 9
[2] outside of if block, a is: 10
[1] outside of if block, a is: 9
[0] outside of if block, a is: 9
这种方法会满足您的需求吗?