你好,游客 登录 注册 搜索
背景:
阅读新闻

OpenMP Tutorial学习笔记(8)OpenMP指令之同步构造(Synchronization Constructs) - Augusdi的专栏

[日期:2013-04-15] 来源:  作者: [字体: ]

OpenMP Tutorial:https://computing.llnl.gov/tutorials/openMP/#Synchronization

同步构造:Synchronization Constructs

(1)什么是同步构造?

OpenMP用于并行编程,自然,就会有“数据竞争”等相关问题。所以,OpenMP也提供了一些同步构造的指令,用于进行同步(synchronization)。

(2)同步构造之master指令

指令功能:master指令指定一个区域只会被一个team中的主线程(master thread)执行,team内的其他线程都会忽略此区域。此指令没有隐含的等待。

指令格式:

  1. #pragma omp master  newline  
  2.    structured_block  
指令限制:master指令不允许跳出或跳进(goto等)master块。

(3)同步构造之critical指令

指令功能:critical指令指定某一区域的代码每次只能同时被一个线程执行。

指令格式:

  1. #pragma omp critical [ (name) ]  newline  
  2.    structured_block  

指令限制:critical指令不允许跳出或跳进(goto等)critical块。

说明:

如果一个执行正在执行critical块的代码,并且其他线程也达到了critical块,那么这个线程会阻塞等待,直到第一个线程执行完critical的代码。其实,critical就相当于简化的win32多线程中的临界区或锁的概念。

实例:

  1. #include "stdafx.h"  
  2.   
  3. int g;  
  4. #define ADD_COUNT   100000  
  5.   
  6. int main(int argc, char *argv[])  
  7. {  
  8.     printf("Masterthread started\n\n");  
  9.   
  10. #pragma omp parallel  
  11.     {  
  12.         for (int i = 0; i < ADD_COUNT;i ++)  
  13.         {  
  14. #pragma omp critical  
  15.             g = g + 1;      // Will cause data races without critical directive.  
  16.         }  
  17.     }// End of parallel region  
  18.   
  19.     printf("g = %d\n",g);  
  20.     printf("Expected g = %d\n",ADD_COUNT*4);        // Assume the thread number is 4.  
  21.   
  22.     printf("Masterthread finished\n");  
  23.   
  24.     return(0);  
  25. }  
这里,如果不使用critical,得到的结果将会不正确,就是由于数据竞争引起的。

下面的例子是同样的数据竞争的例子,这两个例子比较一下也可以加深对parallel和parallel for的区别。

  1. #include "stdafx.h"  
  2.   
  3. int g;  
  4. #define ADD_COUNT   1000000  
  5.   
  6. int main(int argc, char *argv[])  
  7. {  
  8.     printf("Masterthread started\n\n");  
  9.   
  10. #pragma omp parallel for  
  11.         for (int i = 0; i < ADD_COUNT;i ++)  
  12.         {  
  13. #pragma omp critical  
  14.             g = g + 1;      // Will cause data races without critical directive.  
  15.         } // End of parallel for region  
  16.   
  17.     printf("g = %d\n",g);  
  18.     printf("Expected g = %d\n",ADD_COUNT);  
  19.   
  20.     printf("Masterthread finished\n");  
  21.   
  22.     return(0);  
  23. }  

关于name选项:在critical中,还有一个name的参数,critical可以指定代码段的name,同一个name的代码段被当成同一个代码段,所有未命名的代码段都当作一个代码段。

下面的两个例子对比来理解name的作用:

  1. #include "stdafx.h"  
  2.   
  3. int g;  
  4. int g1;  
  5. #define ADD_COUNT   1000000  
  6.   
  7. int main(int argc, char *argv[])  
  8. {  
  9.     printf("Masterthread started\n\n");  
  10.   
  11. #pragma omp parallel for  
  12.         for (int i = 0; i < ADD_COUNT;i ++)  
  13.         {  
  14. #pragma omp critical (namex)  
  15.             g = g + 1;      // Will cause data races without critical directive.  
  16.   
  17. #pragma omp critical (namey)  
  18.             g1 = g1 + 1;        // Will cause data races without critical directive.  
  19.         } // End of parallel for region  
  20.   
  21.     printf("g = %d, g1 = %d\n",g, g1);  
  22.     printf("Expected g = g1 = %d\n",ADD_COUNT);  
  23.   
  24.     printf("Masterthread finished\n");  
  25.   
  26.     return(0);  
  27. }  
这个例子定义了两个全局变量g和g1,分别运算,所以可以使用两个critical进行同步,那么第一个线程在操作namex段的代码(g)的时候,另一个线程(第二个线程)可能在等待,然后,第一个线程进入namey代码段,那么此时第二个线程就可以执行namex段的代码了,由于分别是g和g1的操作,这样是完全允许同时进行的。然而,下面的例子,使用不同的命名的critical,就会导致结果错误:

  1. #include "stdafx.h"  
  2.   
  3. int g;  
  4. int g1;  
  5. #define ADD_COUNT   1000000  
  6.   
  7. int main(int argc, char *argv[])  
  8. {  
  9.     printf("Masterthread started\n\n");  
  10.   
  11. #pragma omp parallel for  
  12.     for (int i = 0; i < ADD_COUNT;i ++)  
  13.     {  
  14. #pragma omp critical (namex)  
  15.         g = g + 1;  
  16.   
  17. #pragma omp critical (namey)  
  18.         g = g + 1;          // Still data races!          
  19.     } // End of parallel for region  
  20.   
  21.     g1 = g;  
  22.     printf("g = %d, g1 = %d\n",g, g1);  
  23.     printf("Expected g = g1 = %d\n",ADD_COUNT);  
  24.   
  25.     printf("Masterthread finished\n");  
  26.   
  27.     return(0);  
  28. }  

(4)同步构造之barrier指令

指定作用:barrier是最简单的同步指令了,用于同步team中的所有线程。当一个线程达到了barrier后,它会在此处等待知道其它所有线程都执行到此处。

指令格式:

  1. #pragma omp barrier  newline  

(5)同步构造之taskwait指令

OpenMP3.0新增加的指令,和task构造指令相关。

(6)同步构造之atomic指令

指令作用:指定某一内存位置必须原子操作的形式更新。相当于简化的critical的使用。

指令格式:

  1. #pragma omp atomic  newline  
  2.    statement_expression  

说明:atomic后面不是一个structed bock,而是一个表达式。atomic的使用有一些格式要求,具体参考OpenMP spec说明。

(7)同步构造之flush指令

指令作用:指定一个同步点,在此处,线程变量会写入到真实的内存中去。

指令格式:

  1. #pragma omp flush (list)  newline  

(8)同步构造之ordered指令

指定for循环迭代和串行一样的顺序执行。

只能用于指令了ordered子句的for指令。

指令格式:

  1. #pragma omp for ordered [clauses...]  
  2.    (loop region)  
  3. #pragma omp ordered  newline  
  4.    structured_block  
  5.    (endo of loop region)  


说明:

对于基本的使用,master,critical,barrier指令需要掌握和理解,其他指令,在以后逐步理解。





收藏 推荐 打印 | 录入:admin | 阅读:
相关新闻