OpenMP模式下多线程文件操作(四)

线程同步之互斥锁函数

前文介绍了互斥锁同步的两种方法:atomic和critical,本章介绍OpenMP提供的互斥锁函数。互斥锁函数类似于Windows、Linux下的mutex。

1. 互斥锁函数

  函数声明                                                                   功能

  void omp_init_lock(omp_lock*)                               初始化互斥器

  void omp_destroy_lock(omp_lock*)                        销毁互斥器

  void omp_set_lock(omp_lock*)                               获得互斥器

  void omp_unset_lock(omp_lock*)                           释放互斥器

  void omp_test_lock(omp_lock*)                              试图获得互斥器,如果获得成功则返回true,否则返回false

2. 互斥锁示例

   

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <iostream>   
  2. #include <omp.h>   
  3.   
  4. static omp_lock_t lock;  
  5.   
  6. int main()  
  7. {  
  8.    omp_init_lock(&lock); //初始化互斥锁   
  9.   
  10. #pragma omp parallel for   
  11.    for(int i = 0; i < 5; ++i)  
  12.    {  
  13.       omp_set_lock(&lock);   //获得互斥器   
  14.        std::cout << omp_get_thread_num() << "+" << std::endl;  
  15.       std::cout << omp_get_thread_num() << "-" << std::endl;  
  16.       omp_unset_lock(&lock); //释放互斥器   
  17.     }  
  18.   
  19.    omp_destroy_lock(&lock);  //销毁互斥器   
  20.     return 0;  
  21. }  

上边的示例对for循环中的所有内容进行加锁保护,同时只能有一个线程执行for循环中的内容。

线程1或线程2在执行for循环内部代码时不会被打断。如果删除代码中的获得锁释放锁的代码,则相当于没有互斥锁。

互斥锁函数中只有omp_test_lock函数是带有返回值的,该函数可以看作是omp_set_lock的非阻塞版本。

线程同步之事件同步机制

1. 引言

前边已经提到,线程的同步机制包括互斥锁同步和事件同步。互斥锁同步包括atomic、critical、mutex函数,其机制与普通多线程同步的机制类似。而事件同步则通过nowait、sections、single、master等预处理指示符声明来完成。

2. 隐式栅障

      在开始之前,先介绍一下并行区域中的隐式栅障。

      栅障(Barrier)是OpenMP用于线程同步的一种方法。线程遇到栅障时必须等待,直到并行的所有线程都到达同一点。

      注意:

      在任务分配for循环和任务分配section结构中隐含了栅障,在parallel, for, sections, single结构的最后,也会有一个隐式的栅障。

隐式的栅障。

      隐式的栅障会使线程等到所有的线程继续完成当前的循环、结构化块或并行区,再继续执行后续工作。可以使用nowait去掉这个隐式的栅障。

3. nowait事件同步

    nowait用来取消栅障,其用法如下:

    #pragma omp for nowait  //不能使用#pragma omp parallel for nowait

    或

    #pragma omp single nowait

    示例:

   

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <iostream>   
  2. #include <omp.h>   
  3.   
  4. int main()  
  5. {  
  6.     #pragma omp parallel   
  7.    {  
  8.       #pragma omp for nowait   
  9.       for(int i = 0; i < 1000; ++i)  
  10.       {  
  11.          std::cout << i << "+" << std::endl;  
  12.       }  
  13.   
  14.       #pragma omp for   
  15.       for(int j = 0; j < 10; ++j)  
  16.       {  
  17.          std::cout << j << "-" << std::endl;  
  18.       }  
  19.    }  
  20.    return 0;  
  21. }  

运行程序,可以看到第一个for循环的两个线程中的一个执行完之后,继续向下执行,因此同时打印了第一个循环的+和第二个循环的-。

如果去掉第一个for循环的nowait生命,则第一个for循环的两个线程都执行完之后,才开始同时执行第二个for循环。也就是说,通过#pragma omp for声明的for循环结束时有一个默认的隐式栅障。

4. 显示同步栅障 #pragma omp barrier

     

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <iostream>   
  2. #include <omp.h>   
  3.   
  4. int main()  
  5. {  
  6.     #pragma omp parallel   
  7.     {  
  8.         for(int i = 0; i < 100; ++i)  
  9.         {  
  10.             std::cout << i << "+" << std::endl;  
  11.         }  
  12.           
  13.         #pragma om barrier   
  14.         for(int j = 0; j < 10; ++j)  
  15.         {  
  16.             std::cout << j << "-" << std::endl;  
  17.         }  
  18.     }  
  19.   
  20.     return 0;  
  21. }  

运行程序,可以看出两个线程执行了第一个for循环,当两个线程同时执行完第一个for循环之后,在barrier处进行了同步,然后执行后边的for循环。

5. master事件同步

    通过#pragma om master来声明对应的并行程序块只有主线程完成。

   

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <iostream>   
  2. #include <omp.h>   
  3.   
  4. int main()  
  5. {  
  6. #pragma omp parallel   
  7. {  
  8.    #pragma omp master   
  9.    {  
  10.       for(int j = 0; j < 10; ++j)  
  11.       {  
  12.          std::cout << j << "-" << std::endl;  
  13.       }  
  14.    }  
  15.   
  16.    std::cout << "This will printed twice." << std::endl;  
  17. }  
  18.    return 0;  
  19. }  

运行程序,可以看到,进入parallel声明的并行区域之后,创建了两个线程。主线程执行了for循环,而另一个线程没有执行for循环,而直接进入了for循环之后的打印语句,然后执行for循环的线程随后还会再执行一次后边的打印语句。

6. sections用来指定不同的线程执行不同的部分

    下面通过一个实例来说明其使用方法:

    

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include <iostream>   
  2. #include <omp.h>   
  3.   
  4. int main()  
  5. {  
  6.   
  7. //声明该并行区域分为若干个section,section之间的运行顺序为并行   
  8. //的关系   
  9. #pragma omp parallel sections   
  10.    for(int i = 0; i < 5; ++i)  
  11.    {  
  12.        std::cout << i << "+" << std::endl;  
  13.    }  
  14.   
  15. #pragma omp section   //第一个section,由某个线程单独完成   
  16.     for(int j = 0; j < 5; ++j)  
  17.    {  
  18.        std::cout << j << "-" << std::endl;  
  19.    }  
  20.   
  21.    return 0;  
  22. }  

可以看到,并行区域中有两个线程,所以两个section同时执行。

原文地址:https://www.cnblogs.com/BIGFOOT/p/2162232.html