OpenMP之同步结构(master、critical、barrier、ordered)

OpenMP的同步结构(Synchronization Constructs)指令包master、critical、barrier、atomic、flush、ordered等

1. master指令
  • master指令指定的区域只由主线程执行,团队中其他线程都跳过该区域代码
  • 本指令没有隐含的barrier,即其他线程不用再master区域结束处同步,可立即执行后续代码。
    代码示例如下
1
2
3
4
5
6
7
8
9
#pragma omp parallel
    {
    #pragma omp master
        {
            printf("in master thread %d\n", omp_get_thread_num());
        }

        printf("out master thread %d\n", omp_get_thread_num());
    }

运行结果如下,可见只有主线程0执行了master区域代码。
在这里插入图片描述

2. critical指令
  • critical指令指定的代码区域,一次只能由一个线程执行。
  • 如果一个线程当前正在一个critical区域内执行,而另一个线程到达该区域并试图执行它,它将阻塞,直到第一个线程退出该区域。

其语法格式如下

1
2
3
#pragma omp critical [ name ]  newline

   structured_block

其中有一个可选名称选项,该选项允许存在多个不同的critical区域,其特性如下:

  • 该名称充当critical区域全局标识符,相同名称的不同临界区域被视为同一区域。
  • 所有未命名的critical区域均视为同一区域。
    critical用法很简单,下面演示下命名critical的特性
2. 1 不同名称的多个critical区域

代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#pragma omp parallel sections
    {
    #pragma omp section
        {
            #pragma omp critical (critical1)
            {
                for (int i=0; i < 5; i++)
                {
                    printf("section1 thread %d excute i = %d\n", omp_get_thread_num(), i);
                    Sleep(200);
                }
            }

        }

    #pragma omp section
        {
            #pragma omp critical (critical2)
            {
                for (int j=0; j < 5; j++)
                {  
                    printf("section2 thread %d excute j = %d\n", omp_get_thread_num(), j);
                    Sleep(200);
                }
            }
        }

    }

运行结果如下,由于两个section中采用了不同名字的critical,所以两个线程能分别同时运行各自的临界区代码。
在这里插入图片描述

2. 2 相同名称的多个critical区域

代码示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#pragma omp parallel sections
    {
    #pragma omp section
        {
            #pragma omp critical (critical1)
            {
                for (int i=0; i < 5; i++)
                {
                    printf("section1 thread %d excute i = %d\n", omp_get_thread_num(), i);
                    Sleep(200);
                }
            }

        }

    #pragma omp section
        {
            #pragma omp critical (critical1)
            {
                for (int j=0; j < 5; j++)
                {  
                    printf("section2 thread %d excute j = %d\n", omp_get_thread_num(), j);
                    Sleep(200);
                }
            }
        }

    }

运行结果如下,两个section本来是两个线程并行处理的,但由于采用了相同名字的critical将两个区域的代码包围,根据前面所说的相同名字的不同临界区属于同一区域,只有一个线程能进入,因此两个section线程是串行的。
尤其要注意如果多个临界区未命名,则也属于同一区域,那么只有一个线程能进入其中一个临界区。
在这里插入图片描述

3. barrier指令
  • barrier指令同步团队中的所有线程。
  • 组内任何线程到达barrier指令时将在该点等待,直到所有其他线程都到达该barrier处为止。然后所有线程才继续并行执行后续代码。
    代码示例如下
1
2
3
4
5
6
#pragma omp parallel
    {
        printf("thread %d excute first print\n", omp_get_thread_num());
    #pragma omp barrier
        printf("thread %d excute second print\n", omp_get_thread_num());
    }

当无barrier和有barrier时,运行结果分别如下。可见,有barrier时,各线程在完成第一条打印后,会在barrier后同步后,在执行第二条打印。
在这里插入图片描述

4. ordered指令
  • ordered指令指定区域的循环迭代将按串行顺序执行,与单个处理器处理结果顺序一致
  • ordered指令只能用在for或parallel for中
    代码示例如下
1
2
3
4
5
6
7
8
9
10
11
#pragma omp parallel
    {
    #pragma omp for ordered
        for (int i = 0; i < 10; ++i)
        {
        #pragma omp ordered
            {
                printf("thread %d excute i = %d\n", omp_get_thread_num(), i);
            }
        }
    }

运行结果如下,可以看出因为增加了ordered指令,for循环是按照i从小到大顺序执行的。
在这里插入图片描述