【OpenMP学习笔记】1:基本使用和critical指令

2023-12-09 14:48

本文主要是介绍【OpenMP学习笔记】1:基本使用和critical指令,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

MPI是可以针对分布式内存,在进程级别实现并行的API;OpenMP则是针对共享内存,在线程级别实现并行的API。

基本使用

不同于MPI的init和finalize,OpenMP用携带了parallel指令的预处理指令指示接下来的一个代码块被多个线程执行。

OpenMP预处理指令-(携带)->OpenMP指令-(携带)->OpenMP指令的子句。

隐式路障是OpenMP的一个特点,所有执行这个代码块的线程在同一个线程组中以隐式路障隐式同步。

#include<stdio.h>
#include<stdlib.h>//检查是否定义了_OPENMP预处理宏,即编译器是否支持OpenMP
#ifdef _OPENMP 
#include<omp.h> //支持时包含OpenMP头文件
#endifint main(int argc,char *argv[])
{//stdlib的strtol()函数把字符串转换成整形数//参数1:字符串首地址,跳过前面的空格符,遇到数字或正负符号才开始做转换//参数2:用于引用返回遇到非数字或'\0'结束转换时的字符指针所在地址//参数3:转换至几进制,取值2~36或0,取0时即默认为10进制int thrdCnt=strtol(argv[1],NULL,0);printf("strtol()函数从主函数参数解析得的线程数:%d\n",thrdCnt);
//# pragma omp指示后面接的是OpenMP的预处理指令//parallel指令表明之后的结构化代码块被多个线程执行//num_threads(线程数)子句指定执行后代码块的线程数
#   pragma omp parallel num_threads(thrdCnt){//如果不支持OpenMP,前面没有包含omp.h,则里面的函数也不可用//所以要在所有使用了omp.h的代码处做包含检查
#       ifdef _OPENMP//omp_get_thread_num()函数获得该线程的编号int myThrdNm=omp_get_thread_num();//omp_get_num_threads()函数获得总的线程数int allNmThrds=omp_get_num_threads();
#       else//当不能使用OpenMP时,只有一个线程,线程号是0int myThrdNm=0;//当不能使用OpenMP时,只有一个线程,线程数目是1int allNmThrds=1;
#       endif//为了验证"隐式路障",此处sleep()不同的时间,不妨按线程编号延时sleep(myThrdNm*3);//C语言里sleep()的单位是秒printf("我是%d/%d\n",myThrdNm,allNmThrds);}//隐式路障:完成代码块的线程要等待其它所有线程完成此代码块printf("路障解除了\n");return 0;
}

输出

[lzh@hostlzh OpenMP]$ !gcc
gcc -fopenmp -o hello.o hello.c
[lzh@hostlzh OpenMP]$ ./hello.o 4
strtol()函数从主函数参数解析得的线程数:4
我是0/4
我是1/4
我是2/4
我是3/4
路障解除了
[lzh@hostlzh OpenMP]$

critical指令

OpenMP里变量的作用域可以按照块内是否共享分为共享作用域私有作用域。对于并行代码块而言,在块前声明的变量缺省作用域是共享的(不过这个”共享”当然不能穿透函数)。

某些地方未使用critical时可能存在的问题

当块内共同操作了共享的资源时,不对其做互斥保护就可能会在运行时出问题。因为只能保证一次汇编级的指令是原子的,甚至不能保证一条C语言语句在并发执行过程中不会发生线程的切换,在并行的情况下就更加危险了:

#include<stdio.h>
#include<stdlib.h>
#include<omp.h>int main(int argc,char *argv[])
{int a=20;int thrdCnt=strtol(argv[1],NULL,10);
#   pragma omp parallel num_threads(thrdCnt)    a=a-1;//并行块中访问了共享变量aprintf("a=%d\n",a);//最终输出看一下return 0;
}

输出

[lzh@hostlzh OpenMP]$ !gcc
gcc -fopenmp -o test1.o test1.c
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=6
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5

才执行了这么几次就发生了出错,可见问题严重。

使用critical时
#include<stdio.h>
#include<stdlib.h>
#include<omp.h>int main(int argc,char *argv[])
{int a=20;int b=20;int thrdCnt=strtol(argv[1],NULL,10);
#   pragma omp parallel num_threads(thrdCnt){//OpenMP指令critical安排线程对接下来的代码块互斥访问
#       pragma omp criticala=a-1;//共享变量a受critical保护互斥访问b=b-1;//共享变量b未受critical保护}printf("a=%d\nb=%d\n",a,b);//最终输出看一下return 0;
}

输出

[lzh@hostlzh OpenMP]$ !gcc
gcc -fopenmp -o test1.o test1.c
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=6
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ ./test1.o 15
a=5
b=5
[lzh@hostlzh OpenMP]$ 

可见受critical指令保护的a能够正常执行了,而不受保护的b仍然会发生前面的问题。

这篇关于【OpenMP学习笔记】1:基本使用和critical指令的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/474087

相关文章

Redis持久化机制之RDB与AOF的使用

《Redis持久化机制之RDB与AOF的使用》:本文主要介绍Redis持久化机制之RDB与AOF的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Redis持久化机制-RDB与AOF一、RDB持久化机制1、RDB简介2、RDB的工作原理3、RDB的优缺点4

使用Python实现实时金价监控并自动提醒功能

《使用Python实现实时金价监控并自动提醒功能》在日常投资中,很多朋友喜欢在一些平台买点黄金,低买高卖赚点小差价,但黄金价格实时波动频繁,总是盯着手机太累了,于是我用Python写了一个实时金价监控... 目录工具能干啥?手把手教你用1、先装好这些"食材"2、代码实现讲解1. 用户输入参数2. 设置无头浏

Spring Boot 常用注解详解与使用最佳实践建议

《SpringBoot常用注解详解与使用最佳实践建议》:本文主要介绍SpringBoot常用注解详解与使用最佳实践建议,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、核心启动注解1. @SpringBootApplication2. @EnableAutoConfi

mysql递归查询语法WITH RECURSIVE的使用

《mysql递归查询语法WITHRECURSIVE的使用》本文主要介绍了mysql递归查询语法WITHRECURSIVE的使用,WITHRECURSIVE用于执行递归查询,特别适合处理层级结构或递归... 目录基本语法结构:关键部分解析:递归查询的工作流程:示例:员工与经理的层级关系解释:示例:树形结构的数

Redis中RedisSearch使用及应用场景

《Redis中RedisSearch使用及应用场景》RedisSearch是一个强大的全文搜索和索引模块,可以为Redis添加高效的搜索功能,下面就来介绍一下RedisSearch使用及应用场景,感兴... 目录1. RedisSearch的基本概念2. RedisSearch的核心功能(1) 创建索引(2

Redis中HyperLogLog的使用小结

《Redis中HyperLogLog的使用小结》Redis的HyperLogLog是一种概率性数据结构,用于统计唯一元素的数量(基数),本文主要介绍了Redis中HyperLogLog的使用小结,感兴... 目录 一、HyperlogLog 是什么?️ 二、使用方法1. 添加数据2. 查询基数China编程3.

Linux系统调试之ltrace工具使用与调试过程

《Linux系统调试之ltrace工具使用与调试过程》:本文主要介绍Linux系统调试之ltrace工具使用与调试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、ltrace 定义与作用二、ltrace 工作原理1. 劫持进程的 PLT/GOT 表2. 重定

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能

《POI从入门到实战轻松完成EasyExcel使用及Excel导入导出功能》ApachePOI是一个流行的Java库,用于处理MicrosoftOffice格式文件,提供丰富API来创建、读取和修改O... 目录前言:Apache POIEasyPoiEasyExcel一、EasyExcel1.1、核心特性

Java 如何创建和使用ExecutorService

《Java如何创建和使用ExecutorService》ExecutorService是Java中用来管理和执行多线程任务的一种高级工具,可以有效地管理线程的生命周期和任务的执行过程,特别是在需要处... 目录一、什么是ExecutorService?二、ExecutorService的核心功能三、如何创建