如何在 OpenMP 中正确使用部分锁?

How to use locks in sections correctly way in OpenMP?

我必须将以下代码与 2 个锁并行化并且输出应该是有序的:
Hello
World
Bye
但是当我 运行 时,线程随机地完成工作。

#include <omp.h>
#include <stdio.h>

int main()
{
    int p;
    omp_lock_t lock;
    omp_init_lock(&lock);
    #pragma omp parallel sections default(shared)
    {
        
        #pragma omp section
        {
            p = omp_get_thread_num();
            omp_set_lock(&lock);
            printf("Th%d: Hello\n",p);
            omp_unset_lock(&lock);
        }

        #pragma omp section
        {   
            p = omp_get_thread_num();
            omp_set_lock(&lock);
            printf("Th%d: World\n",p);
            omp_unset_lock(&lock);
        }

        #pragma omp section
        {
            p = omp_get_thread_num();
            printf("Th%d: Bye\n",p);
        }
    }
    omp_destroy_lock(&lock);
    return 0;
}

来自我的热门评论:

你的锁 gua运行你的线程的顺序--只有原子访问stdout

因此,对于后者,(例如)您将始终获得“整行”输出:

Hello\n
World\n
Bye\n

而且,不是

HeWoByllo\ne\nrld\n

但是,您 可以 得到:

World\n
Bye\n
Hello\n

另外,default(shared)是否意味着p是共享的?我认为 p 需要 private [或者需要在临界区内设置]。

此外,第 3 部分不进行任何锁定。

我只是 运行 程序,并且在所有消息上都收到 Th7,所以 [AFAICT],p 是否需要 私人。


解决这个问题的一种方法是使用“票证锁定”[*]。

编辑: 重读你的问题后,强调 two 锁可能表示替代解决方案,我在下面的更新中添加了.

因为我们无法预测p会是什么,所以每个部分都需要一个序号。

例如Hello需要1个,World需要2个,Bye需要3个。

我们需要添加一个共享变量来指定下一个部分。每个部分都必须在该变量上循环[处于锁定状态],直到它匹配其分配的 order/sequence 编号并在退出时递增它以允许序列中的下一个线程到 运行.

[*]“票锁”仿照(例如)一家面包店,当您进入面包店时“取号”并在标牌上写着:“现在服务......”时购买东西见:https://en.wikipedia.org/wiki/Ticket_lock

一般来说,票锁的好处之一是它 gua运行tees 进步和平衡访问。在实践中,重试次数相对small/infrequent。如果使用 stdatomic.h 原语实现,则尤其如此。

无论如何,这是我想出的:

#include <omp.h>
#include <stdio.h>

int main()
{
    int p;
    int owner = 1;
    int more = 1;

    omp_lock_t lock;
    omp_init_lock(&lock);

    #pragma omp parallel sections default(shared) private(p) private(more)
    {

        #pragma omp section
        {
            more = 1;
            while (more) {
                p = omp_get_thread_num();
                omp_set_lock(&lock);

                if (owner == 1) {
                    printf("Th%d: Hello\n",p);
                    more = 0;
                    owner += 1;
                }

                omp_unset_lock(&lock);
            }
        }

        #pragma omp section
        {
            more = 1;
            while (more) {
                p = omp_get_thread_num();
                omp_set_lock(&lock);

                if (owner == 2) {
                    printf("Th%d: World\n",p);
                    more = 0;
                    owner += 1;
                }

                omp_unset_lock(&lock);
            }
        }

        #pragma omp section
        {
            more = 1;
            while (more) {
                p = omp_get_thread_num();
                omp_set_lock(&lock);

                if (owner == 3) {
                    printf("Th%d: Bye\n",p);
                    more = 0;
                    owner += 1;
                }

                omp_unset_lock(&lock);
            }
        }
    }

    omp_destroy_lock(&lock);

    return 0;
}

这是程序输出。 p 值可以从 运行 运行 变化,但现在,顺序 always 相同:

Th1: Hello
Th7: World
Th4: Bye

更新:

上述解决方案是通用的[并且我在实际生产代码中使用过多次],但可能不符合您使用[=93]的要求=]两个把锁。

我能想到的是有两个锁,在进入任何临界区之前由主线程初始化和锁定

第一部分 (Hello) 需要 锁,先行。另外两个部分(WorldBye)将首先分别锁定lock2lock3

Hello 完成后,解锁 lock2

这允许 World 获得锁和 运行。完成后,它会解锁 lock2lock3

lock3 的解锁允许 Bye 获得该锁。它打印然后解锁 lock3

这是我为此想到的:

#include <omp.h>
#include <stdio.h>

int main()
{
    int p;

    omp_lock_t lock2;
    omp_init_lock(&lock2);
    omp_set_lock(&lock2);

    omp_lock_t lock3;
    omp_init_lock(&lock3);
    omp_set_lock(&lock3);

    #pragma omp parallel sections default(shared) private(p)
    {
        #pragma omp section
        {
            p = omp_get_thread_num();

            printf("Th%d: Hello\n",p);

            omp_unset_lock(&lock2);
        }

        #pragma omp section
        {
            p = omp_get_thread_num();
            omp_set_lock(&lock2);

            printf("Th%d: World\n",p);

            omp_unset_lock(&lock2);
            omp_unset_lock(&lock3);
        }

        #pragma omp section
        {
            p = omp_get_thread_num();
            omp_set_lock(&lock3);

            printf("Th%d: Bye\n",p);

            omp_unset_lock(&lock3);
        }
    }

    omp_destroy_lock(&lock2);
    omp_destroy_lock(&lock3);

    return 0;
}