UnixLinuxWindowsOpenMP多线程编程.docx

上传人:b****4 文档编号:4103601 上传时间:2023-05-06 格式:DOCX 页数:27 大小:28.20KB
下载 相关 举报
UnixLinuxWindowsOpenMP多线程编程.docx_第1页
第1页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第2页
第2页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第3页
第3页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第4页
第4页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第5页
第5页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第6页
第6页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第7页
第7页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第8页
第8页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第9页
第9页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第10页
第10页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第11页
第11页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第12页
第12页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第13页
第13页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第14页
第14页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第15页
第15页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第16页
第16页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第17页
第17页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第18页
第18页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第19页
第19页 / 共27页
UnixLinuxWindowsOpenMP多线程编程.docx_第20页
第20页 / 共27页
亲,该文档总共27页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

UnixLinuxWindowsOpenMP多线程编程.docx

《UnixLinuxWindowsOpenMP多线程编程.docx》由会员分享,可在线阅读,更多相关《UnixLinuxWindowsOpenMP多线程编程.docx(27页珍藏版)》请在冰点文库上搜索。

UnixLinuxWindowsOpenMP多线程编程.docx

UnixLinuxWindowsOpenMP多线程编程

Unix_Linux_Windows_OpenMP多线程编程

第三章Unix/Linux多线程编程[引言]本章在前面章节多线程编程基础知识的基础上,着重介绍Unix/Linux系统下的多线

程编程接口及编程技术。

3.1POSIX的一些基本知识

POSIX是可移植操作系统接口(PortableOperatingSystem

Interface)的首字母缩写。

POSIX是基于UNIX的,这一标准意在期望获得源代码级的软件可移植性。

换句话说,为一

个POSIX兼容的操作系统编写的程序,应该可以在任何其它的POSIX操作系统(即使是来自

另一个厂商)上编译执行。

POSIX标准定义了操作系统应该为应用程序提供的接口:

系统调

用集。

POSIX是由IEEE(InstituteofElectricaland

ElectronicEngineering)开发的,

并由ANSI(AmericanNationalStandardsInstitute)和ISO(InternationalStandards

Organization)标准化。

大多数的操作系统(包括WindowsNT)都倾向于开发它们的变体

版本与POSIX兼容。

POSIX现在已经发展成为一个非常庞大的标准族,某些部分正处在开发过程中。

表1-1给

出了POSIX标准的几个重要组成部分。

POSIX与IEEE1003和2003家族的标准是可互换

的。

除1003.1之外,1003和2003家族也包括在表中。

管理POSIX开放式系统环境(OSE)。

IEEE在1995年通过了这项标准。

ISO的

1003.0

版本是ISO/IEC14252:

1996。

被广泛接受、用于源代码级别的可移植性标准。

1003.1提供一个操作系统的C语

1003.1言应用编程接口(API)。

IEEE和ISO已经在1990年通过了这个标准,IEEE在

1995年重新修订了该标准。

一个用于实时编程的标准(以前的P1003.4或POSIX.4)。

这个标准在1993年被

1003.1b

IEEE通过,被合并进ISO/IEC9945-1。

一个用于线程(在一个程序中当前被执行的代码段)的标准。

以前是P1993.4或

1003.1cPOSIX.4的一部分,这个标准已经在1995年被IEEE

通过,归入ISO/IEC

9945-1:

1996。

一个关于协议独立接口的标准,该接口可以使一个应用程序通过网络与另一个应用

1003.1g

程序通讯。

1996年,IEEE通过了这个标准。

一个应用于shell和工具软件的标准,它们分别是操作系统所必须提供的命令处

1003.2理器和工具程序。

1992年IEEE通过了这个标准。

ISO也已经通过了这个标准

(ISO/IEC9945-2:

1993)。

1003.2d改进的1003.2标准。

一个相当于1003.1的Ada语言的API。

在1992年,IEEE通过了这个标准。

1003.5

在1997年对其进行了修订。

ISO也通过了该标准。

一个相当于1003.1b(实时扩展)的Ada语言的API。

IEEE和ISO都已经通过

1003.5b

了这个标准。

ISO的标准是ISO/IEC14519:

1999。

一个相当于1003.1q(协议独立接口)的Ada语言的API。

在1998年,IEEE通

1003.5c

过了这个标准。

ISO也通过了这个标准。

一个相当于1003.1的FORTRAN语言的API。

在1992年,IEEE通过了这个标准,

1003.9

并于1997年对其再次确认。

ISO也已经通过了这个标准。

一个应用于超级计算应用环境框架(ApplicationEnvironment

Profile,AEP)的

1003.10

标准。

在1995年,IEEE通过了这个标准。

一个关于应用环境框架的标准,主要针对使用POSIX接口的实时应用程序。

1003.13

1998年,IEEE通过了这个标准。

1003.22一个针对POSIX的关于安全性框架的指南。

一个针对用户组织的指南,主要是为了指导用户开发和使用支持操作需求的开放式

1003.23

系统环境(OSE)框架

针对指定和使用是否符合POSIX标准的测试方法,有关其定义、一般需求和指导

2003

方针的一个标准。

在1997年,IEEE通过了这个标准。

这个标准规定了针对1003.1的POSIX测试方法的提供商要提供的一些条件。

2003.1

1992年,IEEE通过了这个标准。

一个定义了被用来检查与IEEE1003.2(shell和工具API)是否符合的测试方

2003.2

法的标准。

在1996年,IEEE通过了这个标准。

表3.1POSIX标准的重要组成部分

本章将重点讲述“POSIX线程”,即符合POSIX国际正式标准POSIXl003.1c-1995的部分。

本章假定用户使用的编程语言为ANSIC语言。

3.2POSIX线程库

首先,在编写POSIX多线程C程序时,需要包含头文件’pthread.h’。

POSIX线程函数都

以’pthread_’开头。

在本章中,我们将介绍一下线程操作函数:

POSIX函数描述

pthread_cancel终止另一个线程pthread_create创建一个线程

pthread_detach设置线程以释放资源

pthread_equal测试两个线程ID是否相等pthread_exit退出线程,而不退出进程2pthread_join等待一个线程

pthread_self找出自己的线程ID

表3.2POSIX线程管理函数

3.2.1创建线程

‘pthread_create’函数创建一个线程。

intpthread_createpthread_t*restrictthread,const

pthread_attr_t*restrictattr,void**start_routinevoid*,

void*restrictarg;

参数thread指向保存线程ID的pthread_t结构。

参数attr表示一个封装了线程的各种属

性的属性对象,用来配置线程的运行,如果为NULL,则使新线程具有默认的属性。

线程属

性将在后面的XX节讨论。

第三个参数start_routine是线程开始执行的时候调用的函数的

名字。

这个函数必须具有以下的格式:

void*start_routinevoid*arg;

返回的void指针将被pthread_join函数当做退出状态来处理。

第四个参数arg正是传递给

start_routine函数的参数。

POSIX的pthread_create函数会使创建的线程自动处于可运行

状态,而不需要一个单独的启动操作。

如果成功,pthread_create返回0,如果不成功,pthread_create返回一个非零的错误码。

下表列出了pthread_create的错误形式及相应的错误码

错误原因EAGAIN系统没有创建线程所需的资源,或者创建线

程会超出系统对一个进程中线程总数的限制

EINVALattr参数是无效的

EPERM调用程序没有适当的权限来设定调度策略或

attr指定的参数

表3.3pthread_create的错误形式及相应的错误码

每一个线程可以通过调用函数pthread_self得到本线程的ID(数据结构类型:

pthread_t),

它的形式为:

pthread_tpthread_selfvoid;

由于pthread_t可能是一个结构,因此POSIX提供了一个函数pthread_equal来比较线程

ID是否相等。

这个函数的形式为:

intpthread_equalpthread_tt1,pthread_tt2;3

两个参数t1和t2是两个线程ID,如果它们相等,pthread_equal就返回一个非零值,如果

不相等,则返回0。

3.2.2分离(Detach)和接合(Join)线程

POSIX线程的一个特点是:

除非线程是被分离了的,否则在线程退出时,它的资源是不会被

释放的。

pthread_detach函数用来分离线程:

intpthread_detachpthread_tthread;

它设置线程的内部选项来说明线程退出后,其所占有的资源可以被回收。

参数thread是要

分离的线程的ID。

被分离的的线程退出时不会报告它们的状态。

如果函数调用成功,

pthread_detach返回0,如果不成功,pthread_detach返回一个非零的错误码。

下表列出

了pthread_detach的错误形式及相应的错误码

错误原因

EINVALthread对应的不是一个可分离的线程ESRCH没有ID为thread的线程

表3.4‘pthread_detach’的错误形式及相应的错误码

pthread_join函数可以使调用这个函数的线程等待指定的线程运行完成再继续执行。

它的

形式为:

intpthread_joinpthread_tthread,void**value_ptr;

参数thread为要等待的线程的ID,参数value_ptr为指向返回值的指针提供一个位置,这

个返回值是由目标线程传递给pthread_exit或return的。

如果value_ptr为NULL,调用

程序就不会对目标线程的返回状态进行检索了。

如果函数调用成功,pthread_join返回0,

如果不成功,pthread_join返回一个非零的错误码。

下表列出了pthread_join的错误形式

及相应的错误码

错误原因EINVALthread对应的不是一个可接合的线程

ESRCH没有ID为thread的线程

表3.5pthread_join的错误形式及相应的错误码

如果线程没有被分离,并且执行pthread_joinpthread_self,那么该线程将被一直挂

起,因为这条语句造成了死锁。

有些POSIX的实现可以检测到死锁,并迫使pthread_join

带着错误EDEADLK返回,但是,POSIX并不要求一定要进行这种检测。

43.2.3退出和取消线程

进程的终止可以通过在主函数main中直接调用exit、return、或者通过进程中的任何其

它线程调用exit来实现。

在任何一种情况下,该进程的所有线程都会终止。

如果主线程在

创建了其它线程之后没有工作可做,它就应该阻塞到所有线程都结束为止,或者应该调用

pthread_exitNULL。

有时程序不必等待线程执行完成,这时程序需要使线程中途退出。

POSIX线程库提供了两个

撤销线程的函数pthread_exit和pthread_cancel。

下面对这

两个函数分别进行介绍。

pthread_exit函数可以使调用这个函数的线程中止运行,并且允许线程传递一个指针,这

个指针可以用来指向线程的返回值。

它的形式为:

voidpthread_exitvoid*value_ptr;

连接了这个线程可以获得参数value_ptr的值。

回顾前面介绍的pthread_join函数,这个

函数的参数void**value_ptr,正是保存pthread_exit函数的参数void*value_ptr的地

址。

这里要注意,pthread_exit的参数value_ptr必须指向线程退出后仍然存在的数据。

POSIX没有为pthread_exit定义任何错误。

POSIXdoesn'tdefineanyerrorcodefor‘pthread_exit’

线程也可以通过取消机制迫使其它的线程退出。

线程可以调用函数pthread_cancel来请求

取消另一个线程。

这个函数的形式是:

intpthread_cancelpthread_tthread;

参数thread是要取消的目标线程的线程ID。

该函数并不阻塞调用线程,它发出取消请求后

就返回了。

如果成功,pthread_cancel返回0,如果不成功,pthread_cancel返回一个非

零的错误码。

线程收到一个取消请求时会发生什么情况取决于它的状态和类型。

如果线程处于

PTHREAD_CANCEL_ENABLE状态,它就接受取消请求,如果线程处于PTHREAD_CANCEL_DISABLE

状态,取消请求就会被保持在挂起状态。

默认情况下,线程处于PTHREAD_CANCEL_ENABLE

状态。

pthread_setcancelstate函数用来改变调用线程的取消状态,它的形式为:

intpthread_setcancelstateintstate,int*oldstate;

参数state表示要设置的新状态,参数oldstate为一个指向整形的指针,用于保存线程以

前的状态。

如果成功,该函数返回0,如果不成功,它返回一个非0的错误码。

通常情况下,

线程函数在改变了线程的取消状态之后,应该在执行完某些操作之后恢复线程的取消状态,

5否则,对于其它可能取消该线程的线程而言,取消操作的结果将无法预测,这很可能不利于

程序的正确执行。

当线程将退出作为对取消请求的响应时,取消类型允许线程控制它在什么地方退出。

当它的

取消类型为PTHREAD_CANCEL_ASYNCHRONOUS时,线程在任何时

候都可以响应取消请求。

当它

的取消类型为PTHREAD_CANCEL_DEFERRED时,线程只能在特定的几个取消点上响应取消请

求。

在默认情况下,线程的类型为PTHREAD_CANCEL_DEFERRED。

pthread_setcanceltype函数用来修改线程的取消类型。

它的形式为:

intpthread_setcanceltypeinttype,int*oldtype;

参数type指定线程的取消类型,参数oldtype用来指定保存原来的取消类型的地址。

如果

成功,该函数返回0,如果不成功,它返回一个非0的错误码。

线程可以通过调用pthread_testcancel在代码中的特定的位置上设置一个取消点。

当类型

为PTHREAD_CANCEL_DEFERRED的线程到达这样一个取消点时,就接受挂起的取消请求。

该函

数的形式为:

voidpthread_testcancelvoid;

3.2.4用户级线程与内核级线程

用户级线程user-levelthread和内核级线程kernel-level

thread是两种传统的线程控

制模式。

用户级线程通常都运行在一个现存的操作系统之上。

这些线程对内核来说是不可见

的,它们被封装在进程里,并竞争分配给进程的资源。

线程由一个

线程运行系统来调度,这

个系统是进程代码的一部分。

带有用户级线程的程序通常会连接到一个特殊的库上去,这个

库中的每个库函数都用外套jacket包装起来。

在调用被外套包装的库函数之前,外套函数

要调用线程运行系统来进行线程管理,在调用了被外套包装的库函数之后,外套函数可能也

要进行这样的操作。

这样做的必要性是为了解决下面的情况:

由于read或sleep这样的函数可能会使进程阻塞,

所以它们给用户级线程带来了一个问题,那就是要避免某个线程在调用这些阻塞型函数之

后,整个进程被阻塞。

这就要求用户级线程库用一个无阻塞的版本来替换每一个外套包装的、

潜在的阻塞型调用。

线程运行系统通过测试来查看调用是否会使线程阻塞,如果调用不会阻

塞,运行系统就立即进行调用,但是,如果调用会阻塞,运行系统就会将线程放在一个等待

线程的列表中,将调用添加到一个动作列表中,以便稍后再试,然后挑选另一个线程来运行。

所有这些控制过程对用户和操作系统来说都是不可见的。

用户级线程的开销很低,但是它们也有些缺点。

用户线程模型假

定线程运行系统昀终会重新

获得控制权,这可能会受到CPU绑定线程CPU-boundthread的阻碍。

CPU绑定线程很少执

行库函数调用,这样就会阻止线程运行系统重新获得控制权来调度其它的线程。

程序员必须

要显式地迫使CPU绑定线程在适当的地方放弃对CPU的控制,以避免出现封锁状态。

第二个

问题是,用户级线程只能共享分配给它们的封装进程的处理器资源。

因为线程一次只能运行

6在一个处理器上,这种约束限制了可用的并行总量。

使用线程的主要原因之一就是要利用多

处理器工作站的优势,所以仅使用用户级线程本身并不是一种能让人接受的方法。

对内核级线程来说,内核了解每一个作为可调度实体的线程,这些线程可以在全系统范围内

竞争处理器资源。

内核级线程的调度开销可能和进程自身的调度差不多昂贵,但是,内核级

线程可以利用多处理器的优势。

内核级线程的同步和数据共享比整个进程的同步和数据共享

的开销要低一些,但内核级线程的管理比用户级线程的管理代价更高。

还有一种模型,称作混合线程模型hybridthreadmodel,它通过

提供两个级别的控制,

同时具备了用户级和内核级模型的优点。

用户用用户级线程编写程序,然后说明有多少个内

核可调度实体与这个进程相关。

运行时,将用户级线程映射为系统的可调度实体,以实现并

行。

用户拥有的映射控制级别取决于实现,例如在Sun的Solaris线程实现中,用户级线程

被称为线程,而内核可调度实体被称为轻量级进程lightweight

process。

用户可以指定

由一个特定的轻量级进程来运行制定的线程,或者由一个轻量级进程池来运行一组制定的线

程。

POSIX线程调度模型是一个混合模型,它很灵活,足以在标准的特定实现中支持用户级和内

核级的线程。

模型中包括两级调度?

?

线程级和内核实体级。

线程与用户级线程类似,内核

实体由内核调度。

由线程库来决定它需要多少内核实体,以及它们是如何映射的。

POSIX引入了一个线程调度竞争范围thread-scheduling

contentionscope的概念,这个

概念赋予了程序员一些控制权,使它们可以控制怎样将内核实体映射为线程。

线程的

contentionscope属性可以是PTHREAD_SCOPE_PROCESS,也可以是PTHREAD_SCOPE_SYSTEM。

带有PTHREAD_SCOPE_PROCESS属性的线程与它们所在的进程中的其它线程竞争处理器资源。

POSIX没有说明这样一个线程怎样与它所在的进程中的其它线程竞争,因此

PTHREAD_SCOPE_PROCESS线程可以是严格的用户级线程,或者它们也可以使用某种更复杂的

方式映射到一个内核实体池中去。

带有PTHREAD_SCOPE_SYSTEM

属性的线程很像内核级线程,

他们在全系统范围内竞争处理器资源。

POSIX将PTHREAD_SCOPE_SYSTEM线程和内核实体之

间的映射留给具体实现来完成,但是一种明显的映射方式是,将这样一个线程直接与内核实

体绑定起来。

POSIX线程的具体实现可能支持PTHREAD_SCOPE_PROCESS、或

PTHREAD_SCOPE_SYSTEM或者两者都支持。

3.2.5线程的属性

POSIX将栈的大小和调度策略这样的特征封装到一个pthread_attr_t类型的对象中去,用

面向对象的方式表示和设置特征。

属性对象只在线程创建的时候会对线程产生影响。

编写程

序时可以先创建一个属性对象,然后再将栈的大小和调度策略这样的特征与属性对象关联起

来,之后就可以通过向pthread_create传递相同的线程属性对象来创建多个具有相同特征

的线程。

通过将各种特征组合到单个对象中去,POSIX避免了用大量参数来调用

pthread_create的情况。

表3.6显示的是线程属性的可设置特征及其相关函数,后面我们将对这些特征和函数进行讨

论。

7特征函数

属性对象pthread_attr_destroy

pthread_attr_init

状态pthread_attr_getdetachstatepthread_attr_setdetachstate

栈pthread_attr_getguardsizepthread_attr_setguardsize

pthread_attr_getstack

pthread_attr_setstack

调度pthread_attr_getinheritschedpthread_attr_setinheritsched

pthread_attr_getschedparam

pthread_attr_setschedparam

pthread_attr_getschedpolicy

pthread_attr_setschedpolicy

pthread_attr_getscope

pthread_attr_setscope

表3.6线程属性的可设置特征及其相关函数

函数pthread_attr_init用默认值对一个线程属性对象进行初始化。

pthread_attr_destroy

函数将属性对象的值设为无效的。

被设为无效的属性对象可以再次被初始化为一个新的属性

对象。

pthread_attr_init和pthread_attr_destroy都只有一个参数,即一个指向属性对

象的指针。

这两个函数的形式为:

intpthread_attr_initpthread_attr_t*attr;

intpthread_attr_destroypthread_attr_t*attr;

如果成功,函数返回0,如果不成功,函数返回一个非0的错误码。

大多数针对属性对象的函数都是获取或设置属性对象的属性。

第一个参数是一个指向属性对

象的指针。

对于获取操作,第二个参数是一个指向存放值的位置的指针,而对于设置操作,

第二个参数是属性的设置值。

因此,后面读者可以根据函数参数的名称和类型推断出参数的

含义,我们就不一一介绍了。

3.2.5.1线程状态线程状态的可能取值为PTH

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 自然科学 > 物理

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2