【发布时间】:2015-12-13 23:12:39
【问题描述】:
我正在尝试使用 OpenMP 的锁定结构来实现 counting semaphores,但是在尝试在关键区域内使用 omp_set_lock() 时遇到了问题(程序挂起)。
我正在使用一个简单的生产者-消费者程序来测试实现。这是我想出的:
#include <omp.h>
#include <stdio.h>
#define N 50
int semaphore_count = 0;
omp_lock_t semaphore_lock;
int A[N];
void semaphore_increment()
{
int my_id = omp_get_thread_num();
#pragma omp critical
{
printf("[%lf][%d] semaphore_count %d --> %d\n", omp_get_wtime(), my_id,
semaphore_count, semaphore_count + 1);
semaphore_count++;
if(semaphore_count == 1) {
// Semaphore was previously locked, so unlock it.
printf("[%lf][%d] Releasing lock.\n", omp_get_wtime(), my_id);
omp_unset_lock(&semaphore_lock);
}
}
}
void semaphore_decrement()
{
int my_id = omp_get_thread_num();
#pragma omp critical
{
printf("[%lf][%d] semaphore_count: %d\n", omp_get_wtime(), my_id,
semaphore_count);
if (semaphore_count == 0) {
printf("[%lf][%d] Sleeping\n", omp_get_wtime(), my_id);
omp_set_lock(&semaphore_lock);
}
else {
printf("[%lf][%d] Working\n", omp_get_wtime(), my_id);
// Creating a critical region here instead of in the beginning of
// the function solves the problem.
// #pragma omp critical
// {
semaphore_count--;
// }
if (semaphore_count == 0) {
omp_set_lock(&semaphore_lock);
}
}
}
}
void produce() {
for (int i = 0; i < N; ++i) {
A[i] = i;
#pragma omp flush
semaphore_increment();
}
}
void consume() {
int sum = 0;
for (int i = 0; i < N; ++i) {
semaphore_decrement();
sum += A[i];
}
printf("Sum is: %d\n", sum);
}
int main() {
omp_init_lock(&semaphore_lock);
omp_set_lock(&semaphore_lock);
#pragma omp parallel
{
#pragma omp single nowait
produce();
#pragma omp single nowait
consume();
}
omp_destroy_lock(&semaphore_lock);
return 0;
}
每次消费者线程进入睡眠状态时,此版本的程序都会挂起。如果我修改它以将关键区域减少到代码的较小部分(如程序中的注释所示),那么它就可以工作。
我不明白的是:为什么会这样?似乎只增加信号量的生产者线程停止运行,然后一切都挂起,但我不明白为什么。
【问题讨论】:
-
将某些东西放入临界区并不会使其相对于 不同 临界区中的某些东西具有原子性。你有竞争条件。在减量周围放置一个小的关键部分可能会导致减量被编译为原子读取-修改-写入,因此它意外地是原子的。这不会使您的代码正确,但可能会使比赛窗口变小,因此您的测试不再显示错误。
-
OpenMP 拥有原子性已有一段时间了。它们可用于 inc 和 dec 等。
-
@EOF 的初始评论是错误:所有具有相同名称的
critical区域(未命名的区域都一起考虑)是互斥的。当前 OpenMP 标准的第 2.13.2 章中描述了确切的语义,但本质上,这就是它归结为您的内容。因此,至少在这方面,您的代码是正确的。我还不知道哪里出了问题,但这绝对不是这部分。 -
@Gilles:这太疯狂了,但你说得很对。注意。
-
您的程序不合格。在给定线程中设置锁会使该线程成为锁的所有者。在 OpenMP 中不允许取消设置由另一个线程拥有的锁,这会导致未定义的行为。这就是为什么您的代码总是与英特尔 OpenMP 运行时一起工作,并且总是在我的系统上与 GCC 发生死锁。
标签: c parallel-processing openmp producer-consumer