【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

相关文章

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

python管理工具之conda安装部署及使用详解

《python管理工具之conda安装部署及使用详解》这篇文章详细介绍了如何安装和使用conda来管理Python环境,它涵盖了从安装部署、镜像源配置到具体的conda使用方法,包括创建、激活、安装包... 目录pytpshheraerUhon管理工具:conda部署+使用一、安装部署1、 下载2、 安装3

Mysql虚拟列的使用场景

《Mysql虚拟列的使用场景》MySQL虚拟列是一种在查询时动态生成的特殊列,它不占用存储空间,可以提高查询效率和数据处理便利性,本文给大家介绍Mysql虚拟列的相关知识,感兴趣的朋友一起看看吧... 目录1. 介绍mysql虚拟列1.1 定义和作用1.2 虚拟列与普通列的区别2. MySQL虚拟列的类型2

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

关于@MapperScan和@ComponentScan的使用问题

《关于@MapperScan和@ComponentScan的使用问题》文章介绍了在使用`@MapperScan`和`@ComponentScan`时可能会遇到的包扫描冲突问题,并提供了解决方法,同时,... 目录@MapperScan和@ComponentScan的使用问题报错如下原因解决办法课外拓展总结@

mysql数据库分区的使用

《mysql数据库分区的使用》MySQL分区技术通过将大表分割成多个较小片段,提高查询性能、管理效率和数据存储效率,本文就来介绍一下mysql数据库分区的使用,感兴趣的可以了解一下... 目录【一】分区的基本概念【1】物理存储与逻辑分割【2】查询性能提升【3】数据管理与维护【4】扩展性与并行处理【二】分区的

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

C#使用HttpClient进行Post请求出现超时问题的解决及优化

《C#使用HttpClient进行Post请求出现超时问题的解决及优化》最近我的控制台程序发现有时候总是出现请求超时等问题,通常好几分钟最多只有3-4个请求,在使用apipost发现并发10个5分钟也... 目录优化结论单例HttpClient连接池耗尽和并发并发异步最终优化后优化结论我直接上优化结论吧,

SpringBoot使用Apache Tika检测敏感信息

《SpringBoot使用ApacheTika检测敏感信息》ApacheTika是一个功能强大的内容分析工具,它能够从多种文件格式中提取文本、元数据以及其他结构化信息,下面我们来看看如何使用Ap... 目录Tika 主要特性1. 多格式支持2. 自动文件类型检测3. 文本和元数据提取4. 支持 OCR(光学