多线程,Mutex,及OpenMP编程

1、什么是多线程?

        多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。

1)单进程单线程:一个人在一个桌子上吃菜。
2)单进程多线程:多个人在同一个桌子上一起吃菜。
3)多进程单线程:多个人每个人在自己的桌子上吃菜。

多线程的问题是多个人同时吃一道菜的时候容易发生争抢,例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了。。。此时就必须等一个人夹一口之后,在还给另外一个人夹菜,也就是说资源共享就会发生冲突争抢。

 

2、Mutex(互斥锁)

         A mutex is a lockable object that is designed to signal when critical sections of code need exclusive access(独占访问), preventing other threads with the same protection from executing concurrently and access the same memory locations.

       多线程共用同一段代码。没锁的话,我就锁上了这段代码,然后用完,我就解锁,这样别人就可以用这段代码了。为了保证数据的一致性,mutex一般用于为一段代码加锁,以保证这段代码的原子性(atomic)操作,即:要么不执行这段代码,要么将这段代码全部执行完毕。有点顺序的意思。

 

 

3、OpenMP并行程序

 

一个最为简单的OpenMP代码如下所示:(OpenMP运行时需要在“配置属性”--“C/C++”--"语言"中选择支持OpenMP即可,头文件加上#include

 

 

#include 
#include 

using namespace std;

int main() 
{
	clock_t t1, t2;
	t1 = clock();

#pragma omp parallel for
	for (int i = 0; i < 10; i++)
	{
		cout << "i = " << i << endl;
	}

	t2 = clock();
	cout << "花费时间为" << double(t2 - t1) / CLOCKS_PER_SEC << "s" << endl;

	system("pause");

}

 

 

 

多线程,Mutex,及OpenMP编程_第1张图片 多线程,Mutex,及OpenMP编程_第2张图片
很显然左图是并行运算的结果,右图是没有并行运算的结果,但是为什么并行运算反而花费时间多, 原因是把时间花在开启线程上了。才10个打印序列,本身打印所花费的时间并不多。
还需要说明的一个问题是:#pragma omp parallel for 这条语句是用来指定后面的for循环语句变成并行执行的。当然for循环里的内容必须满足可以并行执行,即每次互不相干,后一次循环不依赖于前面的循环。
 
 
3.OpenMP程序设计
       下面来举几个竞态(race condition)的问题,这是所有多线程编程中比较棘手的问题。当多个线程并行执行时候,有可能多个线程同时对某个变量进行读写操作,从而导致不可预知的结果。摘自如下博客http://www.cnblogs.com/liangliangdetianxia/p/4032659.html。如下面的一个例程:
#pragma omp parallel for
	for (int i = 0; i < 100000; i++)
	{
		sum = sum + a[i % 10];
	}

	cout << "sum= " << sum << endl;
如果注释掉 #pragma omp parallel for ,让程序先按照传统串行的方式执行,sum  =  550000。但是按照并行方式执行后,sum会变成其他值,比如某一次运行sum = 262093。其原因是,当某线程A执行sum = sum + a[i % 10]的同时,另外一个线程B正好在更新sum,而此时A还在用旧的sum做累加,出现的错误。openMP提供了另外一个工具,归约(reduction):
 
	int a[10] = {1,2,3,4,5,6,7,8,9,10};
	int sum = 0;
#pragma omp parallel for reduction(+:sum)
	for (int i = 0; i < 100000; i++)
	{
		sum = sum + a[i % 10];
	}
上面代码里面,我们在#pragma omp parallel for 后面加上了reduction(+:sum),它的意思是告诉编译器:下面的for循环你要分成多个线程跑,但是每个线程都要保存变量sum的拷贝,循环结束后,所有线程把自己的sum累加起来作为最后的输出。reduction虽然很方便,但它只支持一些基本操作,比如+,-,*,&,|,&&,||等。有些情况下,我们既要避免race condition,但涉及到的操作又超出了reduction的能力范围,应该怎么办呢?这就要用到openMP的另一个工具,critical。来看下面的例子,该例中我们求数组a的最大值,将结果保存在max里。
 
	int max = 0;
	int a[10] = { 11,2,33,4,113,20,321,250,689,16 };

#pragma omp parallel for 
	for (int i = 0; i < 10; i++)
	{

		int temp = a[i];
#pragma omp critical
		{
			if (temp > max)
				max = temp;
		
		}	
	}

    cout << "max: " << max << endl;
上例中,for循环还是被自动分成N份来并行执行,但我们用#pragma omp critical将 if (temp > max) max = temp 括了起来,它的意思是:各个线程还是并行执行for里面的语句,但当你们执行到critical里面时,要注意有没有其他线程正在里面执行,如果有的话,要等其他线程执行完再进去执行。这样就避免了race condition问题,但显而易见,它的执行速度会变低,因为可能存在线程等待的情况。有了以上基本知识,对我来说做很多事情都足够了。
故之前的代码也可以等价为:
 
	int sum = 0;
	int a[10] = { 1,2,3,4,5,6,7,8,9,10 };

#pragma omp parallel for
	for (int i = 0; i < 10; i++)
	{
#pragma omp critical
		{
			sum = sum + a[i];
		}
	}
	cout << "sum = " << sum << endl;

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

你可能感兴趣的:(C++)