我目前正在学习线程以及如何使用它们并行代码。我们当前的主题是#pragma omp task.我目前的理解如下:
- 并行代码执行
- 偶然发现#pragma omp任务
- 在任务池 的作用域内抛出代码
- 在 作用域之后执行代码
- 不同的线程被分配任务
- 在任务范围内执行的代码
假设您有以下代码
#pragma omp parallel {
#pragma omp task // Task A
{...
#pragma omp task // Task B
{...
#pragma omp task // Task C
{...
}
#pragma omp taskwait
}
#pragma omp taskwait
}
}
当线程必须#pragma omp tastwait时,线程正在执行任务B。?线程是否强制等待任务C,或者在等待任务C时可以执行其他任务?
我认为它必须等待。我对任务的理解正确吗?
首先,#pragma omp parallel
是一个fork-join指令。这意味着每个线程将执行该部分的内容,因此任务A, B和C被创建N次,其中N是线程数。您需要#pragma omp single
或#pragma omp master
指令,以便仅创建每种类型的1个任务。
任务的调度大部分是由OpenMP标准未指定的。基本上,标准指定何时创建任务以及何时可以(调度点))调度如何受到属性(如依赖)的约束,但不是when/how任务实际上将。这是运行时的工作。事实上,主流运行时使用不同的调度策略。例如,IOMP运行时(Clang/ICC)使用工作窃取方法,而GOMP (GCC)倾向于使用集中式调度程序(1个大队列)。
#pragma omp taskwait
仅适用于包含父任务的当前上下文中。这意味着第一个任务等待只等待任务C,第二个任务等待任务b。当线程等待任务时,它可以执行其他任务,因为taskwait指令是调度点。IOMP就是这样做的。这并不意味着一定要执行其他任务。
你不应该期望完成其他任务。当任务包含阻塞原语时,程序员通常会做出这种假设。这是一个坏主意,因为它通常在不可预测的时间内无缘无故地使用线程(OpenMP运行时无法基于此优化其调度,因为它没有相关信息),而且还因为它可能导致死锁(由于任务依赖关系和运行时的实现)。还要注意的是,taskyield也不执行任务进度(不执行任何操作是taskyield的完全有效实现)。
最后,它的工作方式如下。每个线程创建一个可以由其他线程执行的任务a(这里不太可能)。当一个任务a被执行时,它会为C创建另一个可以被其他线程执行的任务,以此类推。当第一个任务等待在一个线程中执行时,关联的父任务a和B被启动,C被创建并且可能已经被执行。同一个线程可以启动任务A、B和C,因为一个任务可以被中断(可能使用延续)。当taskwait被执行时,在当前B任务中创建的任务C被保证完成(但其他任务的状态是未定义的)。