Hello, World! Hello, World!
可以看得出parallel语句中的代码被执行了四次,说明总共创建了4个线程去执行parallel语句中的代码。
也可以指定使用多少个线程来执行,需要使用num_threads子句: void main(int argc, char *argv[]) {
#pragma omp parallel num_threads(8) {
printf(“Hello, World!, ThreadId=%d\\n”, omp_get_thread_num() );
} }
执行以上代码,将会打印出以下结果: Hello, World!, ThreadId = 2 Hello, World!, ThreadId = 6 Hello, World!, ThreadId = 4 Hello, World!, ThreadId = 0 Hello, World!, ThreadId = 5 Hello, World!, ThreadId = 7 Hello, World!, ThreadId = 1 Hello, World!, ThreadId = 3
从ThreadId的不同可以看出创建了8个线程来执行以上代码。所以parallel指令是用来为一段代码创建多个线程来执行它的。parallel块中的每行代码都被多个线程重复执行。 和传统的创建线程函数比起来,相当于为一个线程入口函数重复调用创建线程函数来创建线程并等待线程执行完。 4、for指令的使用方法
for指令则是用来将一个for循环分配到多个线程中执行。for指令一般可以和parallel指令合起来形成parallel for指令使用,也可以单独用在parallel语句的并行块中。 #pragma omp [parallel] for [子句] for循环语句
先看看单独使用for语句时是什么效果: int j = 0; #pragma omp for
for ( j = 0; j < 4; j++ ){
printf(“j = %d, ThreadId = %d\\n”, j, omp_get_thread_num()); }
执行以上代码后打印出以下结果 j = 0, ThreadId = 0 j = 1, ThreadId = 0 j = 2, ThreadId = 0 j = 3, ThreadId = 0
从结果可以看出四次循环都在一个线程里执行,可见for指令要和parallel指令结合起来使用才有效果:
如以下代码就是parallel 和for一起结合成parallel for的形式使用的: int j = 0;
#pragma omp parallel for for ( j = 0; j < 4; j++ ){
printf(“j = %d, ThreadId = %d\\n”, j, omp_get_thread_num()); }
执行后会打印出以下结果: j = 0, ThreadId = 0 j = 2, ThreadId = 2 j = 1, ThreadId = 1 j = 3, ThreadId = 3
可见循环被分配到四个不同的线程中执行。
上面这段代码也可以改写成以下形式: int j = 0;
#pragma omp parallel {
#pragma omp for
for ( j = 0; j < 4; j++ ){
printf(“j = %d, ThreadId = %d\\n”, j, omp_get_thread_num()); } }
执行以上代码会打印出以下结果: j = 1, ThreadId = 1 j = 3, ThreadId = 3
j = 2, ThreadId = 2 j = 0, ThreadId = 0
在一个parallel 块中也可以有多个for语句,如: int j;
#pragma omp parallel {
#pragma omp for
for ( j = 0; j < 100; j++ ){ ? }
#pragma omp for
for ( j = 0; j < 100; j++ ){ ? }
? }
for 循环语句中,书写是需要按照一定规范来写才可以的,即for循环小括号内的语句要按照一定的规范进行书写,for语句小括号里共有三条语句 for( i=start; i < end; i++)
i=start; 是for循环里的第一条语句,必须写成 “变量=初值” 的方式。如 i=0 i < end;是for循环里的第二条语句,这个语句里可以写成以下4种形式之一: 变量 < 边界值 变量 <= 边界值 变量 > 边界值 变量 >= 边界值
如 i>10 i< 10 i>=10 i>10 等等 最后一条语句i++可以有以下9种写法之一 i++ ++i i-- --i i += inc i -= inc i = i + inc
i = inc + i i = i –inc
例如i += 2; i -= 2;i = i + 2;i = i - 2;都是符合规范的写法。 5 sections和section指令的用法
section语句是用在sections语句里用来将sections语句里的代码划分成几个不同的段,每段都并行执行。用法如下:
#pragma omp [parallel] sections [子句] {
#pragma omp section {
代码块 } }
先看一下以下的例子代码: void main(int argc, char *argv) {
#pragma omp parallel sections { #pragma omp section
printf(“section 1 ThreadId = %d\\n”, omp_get_thread_num()); #pragma omp section
printf(“section 2 ThreadId = %d\\n”, omp_get_thread_num()); #pragma omp section
printf(“section 3 ThreadId = %d\\n”, omp_get_thread_num()); #pragma omp section
printf(“section 4 ThreadId = %d\\n”, omp_get_thread_num()); }
执行后将打印出以下结果: section 1 ThreadId = 0 section 2 ThreadId = 2 section 4 ThreadId = 3 section 3 ThreadId = 1
从结果中可以发现第4段代码执行比第3段代码早,说明各个section里的代码都是并行执行的,并且各个section被分配到不同的线程执行。
使用section语句时,需要注意的是这种方式需要保证各个section里的代码执行时间相差
不大,否则某个section执行时间比其他section过长就达不到并行执行的效果了。 上面的代码也可以改写成以下形式: void main(int argc, char *argv) {
#pragma omp parallel { #pragma omp sections {
#pragma omp section
printf(“section 1 ThreadId = %d\\n”, omp_get_thread_num()); #pragma omp section
printf(“section 2 ThreadId = %d\\n”, omp_get_thread_num()); }
#pragma omp sections {
#pragma omp section
printf(“section 3 ThreadId = %d\\n”, omp_get_thread_num()); #pragma omp section
printf(“section 4 ThreadId = %d\\n”, omp_get_thread_num()); } }
执行后将打印出以下结果: section 1 ThreadId = 0 section 2 ThreadId = 3 section 3 ThreadId = 3 section 4 ThreadId = 1
这种方式和前面那种方式的区别是,两个sections语句是串行执行的,即第二个sections语句里的代码要等第一个sections语句里的代码执行完后才能执行。
用for语句来分摊是由系统自动进行,只要每次循环间没有时间上的差距,那么分摊是很均匀的,使用section来划分线程是一种手工划分线程的方式,最终并行性的好坏得依赖于程序员。
本篇文章中讲的几个OpenMP指令parallel, for, sections, section实际上都是用来如何创建线程的,这种创建线程的方式比起传统调用创建线程函数创建线程要更方便,并且更高效。
当然,创建线程后,线程里的变量是共享的还是其他方式,主线程中定义的变量到了并行块