虽然我在《【OpenMP】Helloworld》(点击打开链接)与《【OpenMP】互斥》(点击打开链接)都提到过,OpenMP可以与多线程同样理解,但同时也提到过,两者的本质是不同的。最明显的区别,OpenMP是预分配任务的,每个CPU核算多少东西要事先平分,而多线程则在分时片之间存在竞争。最能说明的例子是,如果你的OpenMP出现如下的程序段:
#pragma omp parallel for num_threads(4)
for(int i=0;i<200;i++){
break;
}
或者:
#pragma omp parallel num_threads(THREAD_NUM)
while(中断条件){
break;
}
是不能通过编译的,系统会报:error C3010: “break”: 不允许跳出 OpenMP 结构化块。
这是因为,OpenMP在计算之前,会将实现分配CPU,例如如下代码:
#include "iostream"
#include "omp.h"
using namespace std;
int main(){
#pragma omp parallel for num_threads(4)
for(int i=0;i<200;i++){
}
return 0;
}
for的200次迭代会被分成连续的4段,扔给我这台电脑的4个CPU,0-49次迭代由0号CPU计算,50-99次迭代由1号CPU计算……以此类推。这个分配是在这个for被cpu计算前就执行的。
然而存在这样的情况:假设,我们希望这个i计算到第一个能被100整除且非0的数,也就是当i等于100的时候,就停止这个循环,i不要计算下去了。我最后希望得出i=100这个结果,同时用到OpenMP的cpu多核计算。我要用break;去停止,但上面提到过OpenMP的原理决定你无法用break,那该怎么办了。
当然,有人可能问为何,不直接让i自增到100得了,这只是举个例子,很多时候,你并不知道要并行多少次,比如,有个并行任务,就是并行一次将处理列表被处理的元素抹去,直到处理列表没有数位置,我又不知道处理列表到底有多长的时候……
于是,有人就开始疑惑我写成while行不行?为何网上说parallel的用法多以#pragma omp parallel for来举例,能否将在一个while{}前面加上#pragma omp parallel,然后将while的判断条件,改成我要跳出循环的条件。这是不对的,因为这样意思是创建若干个线程,每个线程都将执行后面的while语句,但是while循环并不会在各个线程之间进行分配。也就是说这个while语句会被多个线程重复执行。
那么,修改for循环,改成如下形式可以不?
#include "iostream"
#include "omp.h"
using namespace std;
int main(){
#pragma omp parallel for num_threads(4)
for(int i=0;i<200&&(i!=0&&i%100==0);i++){
}
return 0;
}
这也是不行了,因为上面已经提到过OpenMP中的#pragma omp parallel for,是严格分配CPU的,接下一行的for循环是有严格的格式限制的,这样写会引发:error C3017: OpenMP“for”语句中的终止测试格式不正确。
这是不是,如果涉及并行的中断,就不能用OpenMP呢?不是的,我们可以换种思维:既然OpenMP的并行不能中断,当计算已经满足条件的时候,让OpenMP的并行计算,空转就行了,不让break,又没有不能用continue。continue就是不执行这次循环的意思,较少使用但并不生僻的关键字。
上述程序可以写成如下:
#include "iostream"
#include "omp.h"
using namespace std;
int main(){
int result;//这里设置result来接受i被中断的时候的值
int i=0;
bool finish_flag=false;
#pragma omp parallel for num_threads(4)
for(i=0;i<200;i++){//计算之后无论得到这个i自增到什么值,都不会赋予给int i。int i的值,在#pragma omp parallel for num_threads(4)之后,还是保持不变。
if(finish_flag==true){
continue;
}
if(finish_flag==false){
if(i!=0&&i%100==0){
result=i;
finish_flag=true;
}
}
}
cout<<result;
return 0;
}
或者这样:
#include "iostream"
#include "omp.h"
using namespace std;
int main(){
int result;
bool finish_flag=false;
#pragma omp parallel for num_threads(4)
for(int i=0;i<200;i++){
if(finish_flag==true){
continue;
}
if(finish_flag==false){
if(i!=0&&i%100==0){
result=i;
finish_flag=true;
}
}
}
cout<<result;
return 0;
}
亦或用while的形式,但while的形式需要用到OpenMP的临界区,不然最后算出来的结果是103。for则不需要。
#include "iostream"
#include "omp.h"
using namespace std;
int main(){
int i=0;
bool finish_flag=false;
#pragma omp parallel num_threads(4)
while(!finish_flag){
if(finish_flag==true){
continue;
}
#pragma omp critical
{
if(finish_flag==false){
if(i!=0&&i%100==0){
finish_flag=true;
}
else{
i++;
}
}
}
}
cout<<i;
return 0;
}
感觉while的形式更能够让人理解,不过上述的程序无论写成怎么样,运行结果都是100: