多线程编程——openmp笔记

Notice

使用openmp进行多线程开发,包含一些简单的用法,不包含一些比较难的概念。
使用GCC进行编程,重中之重,编译的时候要加-openmp打开openmp
#pragma…{}宣布大括号里面是并行的

g++ -fopenmp .\test2.cpp -o test

输出不要使用cout,使用printf,不然你的每一行都是乱序,根本没法看。

HelloWorld

最简单的helloworld代码,自动分配线程数量,我的电脑两核四线程,就会有四个线程一起执行。

#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
    #pragma omp parallel
    {
        printf("hello,world\n");
    } 
    return 0;
}
------------------------------------------
hello,world
hello,world
hello,world
hello,world

自己分配线程数

两种方式,把#pragma后面加代表作用域内6个线程,也可以使用我注释掉的那行,代表统一使用六个。那么我明明只有四线程,为什么可以申请六个呢?
这里要搞清楚并行和并发的概念,并行代表真的所有线程同时执行,我四核,最多也就并行4,速度四倍。而并发只是我们看上去在同时进行,就像你同时写两份作业,这写一道那写一道,看上去好像差不多时间完成,但实际上你要花的时间可能比先写完数学再写完英语更加长。但是有的时候我们必须这样,就像你有很多门课,你并不是学完一门换下一门,而是学学这门学学那门,这就是并发。

#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
	//omp_set_num_threads(6);
    #pragma omp parallel num_threads(6)
    {
        printf("hello,world\n");
    } 
    return 0;
}
---------------------------------------
hello,world
hello,world
hello,world
hello,world
hello,world
hello,world

获取进程号

使用omp_get_thread_num()获取目前是那个线程,你可以发现输出顺序不同,多执行几次也不会一样,因为每个线程速度是不一样的。

#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
    omp_set_num_threads(6);
    #pragma omp parallel 
    {
        int id=omp_get_thread_num();
        printf("%d\n",id);
    } 
    return 0;
}
------------------------
3
1
2
0
4
5

你还可以用omp_get_num_threads()获得一共几个线程

加速For循环

两种写法,看到直接使用#pragma omp parallel for后面就不能跟中括号了,要紧跟for

#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
    #pragma omp parallel 
    {
        #pragma omp for
        for(int i=1;i<=10;i++)
        {
            int id = omp_get_thread_num();
            printf("thread: %d num: %d\n",id,i);
        }
    } 
    return 0;
}
#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
    #pragma omp parallel for
        for(int i=1;i<=10;i++)
        {
            int id = omp_get_thread_num();
            printf("thread: %d num: %d\n",id,i);
        }
    return 0;
}
-------------------------
thread: 0 num: 1
thread: 0 num: 2
thread: 1 num: 4
thread: 3 num: 9
thread: 3 num: 10
thread: 0 num: 3
thread: 1 num: 5
thread: 1 num: 6
thread: 2 num: 7
thread: 2 num: 8

有序for

注意这种写法,不是在最外层for加关键词就够了,for里面也要加,另外里面#pragma omp ordered之前都还是并行的,但是在之后就都是串行了,即便是我打的内部大括号后面。所以一般就是为了输出有序

#include <iostream>
#include <omp.h>
using namespace std;
int main()
{
    #pragma omp parallel for ordered
        for(int i=1;i<=10;i++)
        {
            #pragma omp ordered
            {
                int id = omp_get_thread_num();
                printf("thread: %d num: %d\n",id,i);                
            }

        }
    return 0;
}
-----------------------------------
thread: 0 num: 1
thread: 0 num: 2
thread: 0 num: 3
thread: 1 num: 4
thread: 1 num: 5
thread: 1 num: 6
thread: 2 num: 7
thread: 2 num: 8
thread: 3 num: 9
thread: 3 num: 10

schedule(for)

#pragma omp for schedule(static, chunk)
静态每chunk个循环分给一个线程,我也不确定有啥用
#pragma omp for schedule(dynamic, chunk)
动态划分,可能第一个chunk给1,第二个给2,分第三个的时候第一个已经好了再给1

sections,并行执行几个section

不保证顺序

#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
    #pragma omp parallel sections 
    {
        #pragma omp section
        {
            printf("xxx\n");
        }
        #pragma omp section
        {
            printf("yyy\n");
        }     
        #pragma omp section
        {
            printf("zzz\n");
        }     
    }
    return 0;
}
-------------------------------
xxx
yyy
zzz

barrier(所有线程到这停)

#pragma omp barrier前所有进程结束再继续,for和sections都隐含了barrier

#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
    #pragma omp parallel
    {
        int id=omp_get_thread_num();
        printf("id: %d\n",id);
        #pragma omp barrier
        if(id==1)
        {
            printf("111\n");
        }
    }
    return 0;
}
-------------------------
id: 2
id: 0
id: 1
id: 3
111

nowait(线程干完就往后走,不等其他线程)

让for和sections不等到全都结束就继续

#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
    #pragma omp parallel
    {
        #pragma omp for nowait
        for(int i=0;i<=6;i++)
        {
            printf("%d\n",i);
        }
        printf("xxx\n");
    }
    
    return 0;
}
--------------------------------------------
0
1
xxx
6
xxx
4
5
2
3
xxx

single(只执行一次)

以下代码只有一个线程执行,显然没有这行,会有四个continue

#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
    #pragma omp parallel
    {
        #pragma omp for
        for(int i=0;i<=3;i++)
        {
            printf("%d\n",i);
        }
        #pragma omp single
        printf("continue\n");
        #pragma omp for
        for(int i=0;i<=3;i++)
        {
            printf("%d\n",i);
        }
    }
    return 0;
}
--------------------------------
0
2
1
3
continue
2
1
3
0

critical

critical里面只能由一个线程执行,相同的critical变成串行
所有没名字的critical都是一个critical

#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
#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);
				} 
			}
		}

	}
    
    return 0;
}

atomic(声明同一时间一个线程运算此单元)

不加atomic那行,x最终会少。x++包含拿出x,x++,放回去两个操作,可能有线程拿出x还没放回去,另一个就去拿了,那就会少加。

#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
	int x=0;
#pragma omp parallel num_threads(6)
	{
		for(int i=0; i<100000; ++i)
	#pragma omp atomic
        x++;

	printf("%d", x); 
}

数据作用域

Private

private(x) x分别被每一个线程拥有,离开作用域后变回原来的值,就和在里面重新定义了一个是一样的。

#include <iostream>
#include <omp.h>
#include <windows.h>
using namespace std;
int main()
{
	int x=10086;
    #pragma omp parallel private(x)
    {
        x=omp_get_thread_num();
        printf("%d\n",x);
    }
    printf("finished:%d\n",x);
	return 0;
}
------------------------------
1
0
2
3
finished:10086

firstprivate and lastprivate

其实我不太会用,,我宁可里面开新变量,后面再加一个barrier,然后付给外变量
#pragma omp parallel firstprivate(i) private(a)i会使用外层i作为初值
#pragma omp sections lastprivate(i) private(a)会把最后一次迭代的i传回去

shared

大家都是同一个a,b#pragma omp parallel shared(a,b),要注意冲突

default

没看懂

reduction

分成几个线程,每个线程进行sum++之类的操作时候,可能冲突,它现在就是让各自加各自的sum,最后一起加起来,只有这样才能这么快。

#include "iostream"
#include <omp.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
 
int main()
{
	omp_set_num_threads(2);
	long long sum=0;
	clock_t t1=clock();
 
    #pragma omp parallel for reduction(+:sum)
		for(long i=1;i<=100000000;i++)
			sum=sum+i;
 
	clock_t t2=clock();
	printf("sum=%lld\n",sum);
	printf("parallel time=%d\n",(t2-t1));
 
	sum=0;
	t1=clock();
	for(long i=1;i<=100000000;i+=1)
		sum=sum+i;
	t2=clock();
	printf("sum=%lld\n",sum);
	printf("serial time=%d\n",(t2-t1));
	system("pause");
	return 0;
}
-----------------
sum=500000000500000000
parallel time=1357
sum=500000000500000000
serial time=2530
  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值