OpenMP Tutorial学习笔记(8)OpenMP指令之同步构造(Synchronization Constructs)

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

同步构造:Synchronization Constructs

(1)什么是同步构造?

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

(2)同步构造之master指令

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

指令格式:

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

(3)同步构造之critical指令

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

指令格式:

#pragma omp critical [ (name) ]  newline
   structured_block

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

说明:

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

实例:

#include "stdafx.h"

int g;
#define ADD_COUNT	100000

int main(int argc, char *argv[])
{
	printf("Masterthread started\n\n");

#pragma omp parallel
	{
		for (int i = 0; i < ADD_COUNT;i ++)
		{
#pragma omp critical
			g = g + 1;		// Will cause data races without critical directive.
		}
	}// End of parallel region

	printf("g = %d\n",g);
	printf("Expected g = %d\n",ADD_COUNT*4);		// Assume the thread number is 4.

	printf("Masterthread finished\n");

	return(0);
}
这里,如果不使用critical,得到的结果将会不正确,就是由于数据竞争引起的。

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

#include "stdafx.h"

int g;
#define ADD_COUNT	1000000

int main(int argc, char *argv[])
{
	printf("Masterthread started\n\n");

#pragma omp parallel for
		for (int i = 0; i < ADD_COUNT;i ++)
		{
#pragma omp critical
			g = g + 1;		// Will cause data races without critical directive.
		} // End of parallel for region

	printf("g = %d\n",g);
	printf("Expected g = %d\n",ADD_COUNT);

	printf("Masterthread finished\n");

	return(0);
}

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

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

#include "stdafx.h"

int g;
int g1;
#define ADD_COUNT	1000000

int main(int argc, char *argv[])
{
	printf("Masterthread started\n\n");

#pragma omp parallel for
		for (int i = 0; i < ADD_COUNT;i ++)
		{
#pragma omp critical (namex)
			g = g + 1;		// Will cause data races without critical directive.

#pragma omp critical (namey)
			g1 = g1 + 1;		// Will cause data races without critical directive.
		} // End of parallel for region

	printf("g = %d, g1 = %d\n",g, g1);
	printf("Expected g = g1 = %d\n",ADD_COUNT);

	printf("Masterthread finished\n");

	return(0);
}
这个例子定义了两个全局变量g和g1,分别运算,所以可以使用两个critical进行同步,那么第一个线程在操作namex段的代码(g)的时候,另一个线程(第二个线程)可能在等待,然后,第一个线程进入namey代码段,那么此时第二个线程就可以执行namex段的代码了,由于分别是g和g1的操作,这样是完全允许同时进行的。然而,下面的例子,使用不同的命名的critical,就会导致结果错误:

#include "stdafx.h"

int g;
int g1;
#define ADD_COUNT	1000000

int main(int argc, char *argv[])
{
	printf("Masterthread started\n\n");

#pragma omp parallel for
	for (int i = 0; i < ADD_COUNT;i ++)
	{
#pragma omp critical (namex)
		g = g + 1;

#pragma omp critical (namey)
		g = g + 1;			// Still data races!		
	} // End of parallel for region

	g1 = g;
	printf("g = %d, g1 = %d\n",g, g1);
	printf("Expected g = g1 = %d\n",ADD_COUNT);

	printf("Masterthread finished\n");

	return(0);
}

(4)同步构造之barrier指令

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

指令格式:

#pragma omp barrier  newline

(5)同步构造之taskwait指令

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

(6)同步构造之atomic指令

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

指令格式:

#pragma omp atomic  newline
   statement_expression

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

(7)同步构造之flush指令

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

指令格式:

#pragma omp flush (list)  newline

(8)同步构造之ordered指令

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

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

指令格式:

#pragma omp for ordered [clauses...]
   (loop region)
#pragma omp ordered  newline
   structured_block
   (endo of loop region)


说明:

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

你可能感兴趣的:(thread,多线程,编程,list,parallel,newline)