如何在 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
) 需要 无 锁,先行。另外两个部分(World
和Bye
)将首先分别锁定lock2
和lock3
。
Hello
完成后,解锁 lock2
。
这允许 World
获得锁和 运行。完成后,它会解锁 lock2
和 lock3
。
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;
}
我必须将以下代码与 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
) 需要 无 锁,先行。另外两个部分(World
和Bye
)将首先分别锁定lock2
和lock3
。
Hello
完成后,解锁 lock2
。
这允许 World
获得锁和 运行。完成后,它会解锁 lock2
和 lock3
。
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;
}