OpenMP:单个,nowait构造中的共享变量

时间:2016-02-23 19:33:59

标签: c multithreading openmp shared-memory

所以我有一小段代码:

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)),那么一旦另一个线程进入单个块就更新该值是至关重要的。我做错了什么?

3 个答案:

答案 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)

我不相信你想要一个执行你的代码块的线程,而是你想要确保每个线程都不会踩到其他线程' afinished中的数据。这是通过#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

这种方法会满足您的需求吗?