OpenMp之sections用法


section語句是用在sections語句里用來將sections語句里的代碼划分成幾個不同的段

#pragma omp [parallel] sections [子句]
{
   #pragma omp section
   {
            代碼塊
   } 
}
     當存在可選參數#pragma omp parallel sections時,塊中的代碼section才會並行處理,而#pragma omp  sections是串行的程序。詳見下面的代碼:
#include<stdio.h>
#include<stdlib.h>
#include<omp.h>
#include <unistd.h>
int main()
{

   printf("parent threadid:%d\n",omp_get_thread_num());
   #pragma omp  sections
   {
     #pragma omp section
     {
          printf("section 0,threadid=%d\n",omp_get_thread_num());
          sleep(1);
     }
     #pragma omp section
     {
          printf("section 1,threadid=%d\n",omp_get_thread_num());
          //sleep(1);
     }
     #pragma omp section
     {
          printf("section 2,threadid=%d\n",omp_get_thread_num());
          sleep(1);
     }
   }
   #pragma omp parallel sections
   {
      #pragma omp section
     {
          printf("section 3,threadid=%d\n",omp_get_thread_num());
          sleep(1);
     }
      #pragma omp section
     {
          printf("section 4,threadid=%d\n",omp_get_thread_num());
          sleep(1);
     }
      #pragma omp section
     {
          printf("section 5,threadid=%d\n",omp_get_thread_num());
          sleep(1);
     }
   }
 
 return 0;
}
輸出結果為:
parent threadid:0
section 0,threadid=0
section 1,threadid=0
section 2,threadid=0
section 3,threadid=0
section 4,threadid=2
section 5,threadid=1

 

針對上面的代碼,首先應該明確下面幾點:

   1. sections之間是串行的。主線程把section0~2執行完之后才執行的第二個sections。

   2.沒有加parallel的sections里面的section是串行的,為此我專門sleep了一秒,結果顯示0~2都是主線程做的。

   3.第二個sections里面是並行的,進程編號分別為0,2,1。

問題來了,第二部分的0是不是主線程呢?還是第二部分新開的一個線程?為此需要真正輸出每個線程在內核中的線程編號:

#include<stdio.h>
#include<stdlib.h>
#include<omp.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>

int main()
{

   printf("pid:%d,tid=%ld\n",getpid(),syscall(SYS_gettid));
   #pragma omp sections
   {
     #pragma omp section
     {
          printf("section 0,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
     #pragma omp section
     {
          printf("section 1,tid=%ld\n",syscall(SYS_gettid));
          //sleep(1);
     }
     #pragma omp section
     {
          printf("section 2,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
   }
   #pragma omp parallel sections
   {
      #pragma omp section
     {
          printf("section 3,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
      #pragma omp section
     {
          printf("section 4,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
      #pragma omp section
     {
          printf("section 5,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
   }
 
 return 0;
}

 

輸出結果:

pid:7619,tid=7619
section 0,tid=7619
section 1,tid=7619
section 2,tid=7619
section 5,tid=7621
section 4,tid=7619
section 3,tid=7620

從結果中可以看出以下幾點:

  1. OpenMP上說當程序執行到第二個sections是並行的,主線程是休眠的,一直等所有的子線程都執行完畢之后才喚醒,可是在第二個sections中有個線程id和主線程id一致?其實是不一致的,首先從兩者的類型上來看,線程編號是long int的,而進程是int的,數字一致並不能說兩者相同。另外一方面,在linuxthreads時代,線程稱為輕量級進程(LWP),也就是每個線程就是個進程,每個線程(進程)ID不同;而從2.4.10后,采用NPTL(Native Posix Thread Library)的線程庫, 各個線程同樣是通過fork實現的,並且具備同一個父進程。
  2. 主進程id為7619,同時它又有個線程id也是7619,又一次證明在linux中線程進程差別不大。
  3. 猜測主線程並不是休眠,而是將原先的上下文保存,然后自身也作為並行的一份子進行並行程序的執行,當並行程序完成之后,再回復原先的上下文信息。

下面是一個比較復雜的例子

#include<stdio.h>
#include<stdlib.h>
#include<omp.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>

int main()
{
#pragma omp parallel
{
   printf("pid:%d,tid=%ld\n",getpid(),syscall(SYS_gettid));
   #pragma omp sections
   {
     #pragma omp section
     {
          printf("section 0,tid=%ld\n",syscall(SYS_gettid));
          //sleep(1);
     }
     #pragma omp section
     {
          printf("section 1,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
     #pragma omp section
     {
          printf("section 2,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
   }
   #pragma omp sections
   {
      #pragma omp section
     {
          printf("section 3,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
      #pragma omp section
     {
          printf("section 4,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
      #pragma omp section
     {
          printf("section 5,tid=%ld\n",syscall(SYS_gettid));
          sleep(1);
     }
   }
}
 
 return 0;
}

輸出結果:

pid:7660,tid=7660
section 0,tid=7660
section 1,tid=7660
pid:7660,tid=7662
section 2,tid=7662
pid:7660,tid=7663
pid:7660,tid=7661
section 3,tid=7660
section 5,tid=7661
section 4,tid=7662

#pragma omp parallel里面的代碼是並行處理的,但是並不意味着代碼要執行N次(其中N為核數),sections之間是串行的,而並行的實際部分是sections內部的代碼。當線程7660在處理0,1時,因為section1休眠1s,所以section2在此期間會被新的線程進行處理。第一個sections真正處理完成之后,第二個sections才開始並行處理。

另外值得注意的是,printf並不是並行的函數,它是將結果輸出到控制台中,可是控制台資源並不是共享的。當被某個線程占用之后,其余的線程只能等待,拿輸出的結果為例。對於#pragma omp parallel里面的代碼是並行的,可是線程之間還是有先后的次序的,次序和線程的創建時間有關,對於線程7660來說,本身就已經存在了,所以首先獲得printf函數,而直到它執行section0里面的printf時,其他的線程還沒有創建完畢,接着是setion1里面的printf,即使是這個時候有其他的線程創建完成了,也只能等待,在section1中,sleep了1秒鍾,printf函數被新的線程使用,下面也如此。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM