首页 > 解决方案 > 如何在线程执行循环迭代时继续使用master?

问题描述

我被要求用 C 编写一个 OpenMP 程序,以便主线程将工作分配给其他线程,并且在他们处理任务时,主线程应该定期检查它们是否完成,如果没有,它应该增加一个共享多变的。

这是线程任务的函数:

void work_together(int *a, int n, int number, int thread_count) {
#   pragma omp parallel for num_threads(thread_count) \
        shared(a, n, number) private(i) schedule(static, n/thread_count)
    for (long i=0; i<n; i++) {
        // do a task, such as:
        a[i] = a[i] * number;
    }
}

它从 main 调用:

int main(int argc, char *argv[]) {
    int n = atoi(argv[1]);
    int arr[n];
    initialize(arr, n);

    // this will be the shared variable
    int number = 2;
    work_together(arr, n, number, thread_count);

    //I want to write a function or an if to check whether threads are still working
    /* if (threads_still_working()) {
        number++;
        sleep(100);
    }
    */

    printf("There are %d threads\n", omp_get_num_threads());
}

thread_count被初始化为4,我尝试为大n的(> 10000)执行它,但是主线程将始终等待其他线程完成执行for循环,并且只会在work_together()返回时继续主线程: printf() 将总是打印只有一个线程在运行。

现在,有什么方法可以从主线程检查其他线程是否仍在运行,如果它们还在运行,则进行一些递增?

标签: cmultithreadingperformanceparallel-processingopenmp

解决方案


OpenMP 标准可以读到:

当线程遇到并行构造时,会创建一组线程来执行并行区域。遇到并行构造的线程成为新团队的主线程,在新并行区域的持续时间内线程数为零。 新团队中的所有线程,包括主线程,都执行该区域。创建团队后,团队中的线程数在该并行区域的持续时间内保持不变。

因此,使用该子句,#pragma omp parallel for num_threads所有线程都将执行并行工作(计算循环的迭代),这是您不想要的。为了解决这个问题,您可以实现部分功能

`#pragma omp parallel for num_threads`

因为,显式使用上述子句将使编译器自动在团队中的线程之间划分循环的迭代,包括该团队的主线程。代码如下所示:

# pragma omp parallel num_threads(thread_count) shared(a, n, number)
{
      int thread_id = omp_get_thread_num();
      int total_threads = omp_get_num_threads();
      if(thread_id != 0) // all threads but the master thread
      {
        thread_id--; // shift all the ids
        total_threads = total_threads - 1;
        for(long i = thread_id ; i < n; i += total_threads) {
            // do a task, such as:
            a[i] = a[i] * number;
        }
      }
} 

首先,我们确保除线程( if(thread_id != 0))之外的所有线程都执行要并行化的循环,然后我们将循环的迭代分配给其余线程(i.e., for(int i = thread_id ; i < n; i += total_threads))。我选择了 chunk=1 的静态分布,你可以选择不同的,但你必须相应地调整循环。

现在您只需将逻辑添加到:

现在,有什么方法可以从主线程检查其他线程是否仍在运行,如果它们还在运行,则进行一些递增?

为了不泄露太多,我将添加伪代码,您必须将其转换为真实代码才能使其工作:

// declare two shared variable 
// 1) to count the number of threads that have finished working count_thread_finished
# pragma omp parallel num_threads(thread_count) shared(a, n, number)
{
      int thread_id = omp_get_thread_num();
      int total_threads = omp_get_num_threads();
      if(thread_id != 0) // all threads but the master thread
      {
        thread_id--; // shift all the ids
        total_threads = total_threads - 1;
        for(long i = thread_id ; i < n; i += total_threads) {
            // do a task, such as:
            a[i] = a[i] * number;
        }
        // count_thread_finished++
      }
      else{ // the master thread 
          while(count_thread_finished != total_threads -1){
              // wait for a while....
          }
     }
} 

但是请记住,由于变量count_thread_finished在线程之间共享,因此您需要确保在其更新时互斥例如,使用omp atomic),否则您将遇到竞争条件。这应该给你足够的继续前进。

顺便说一句:schedule(static, n/thread_count)大多数情况下不需要,因为默认情况下,大多数 OpenMP 实现已经将循环的迭代(在线程之间)划分为连续的块。


推荐阅读