ImageVerifierCode 换一换
格式:DOCX , 页数:27 ,大小:28.20KB ,
资源ID:4103601      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bingdoc.com/d-4103601.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(UnixLinuxWindowsOpenMP多线程编程.docx)为本站会员(b****4)主动上传,冰点文库仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰点文库(发送邮件至service@bingdoc.com或直接QQ联系客服),我们立即给予删除!

UnixLinuxWindowsOpenMP多线程编程.docx

1、UnixLinuxWindowsOpenMP多线程编程Unix_Linux_Windows_OpenMP多线程编程第三章 Unix/Linux 多线程编程引言本章在前面章节多线程编程基础知识的基础上,着重介绍 Unix/Linux 系统下的多线 程编程接口及编程技术。 3.1 POSIX 的一些基本知识 POSIX 是可移植操作系统接口(Portable Operating System Interface)的首字母缩写。 POSIX 是基于 UNIX 的,这一标准意在期望获得源代码级的软件可移植性。换句话说,为一 个 POSIX 兼容的操作系统编写的程序,应该可以在任何其它的 POSIX 操

2、作系统(即使是来自 另一个厂商)上编译执行。POSIX 标准定义了操作系统应该为应用程序提供的接口:系统调 用集。POSIX是由 IEEE(Institute of Electrical and Electronic Engineering)开发的, 并由 ANSI(American National Standards Institute)和 ISO(International Standards Organization)标准化。大多数的操作系统(包括 Windows NT)都倾向于开发它们的变体 版本与 POSIX 兼容。 POSIX 现在已经发展成为一个非常庞大的标准族,某些部分正处在

3、开发过程中。表 1-1 给 出了 POSIX 标准的几个重要组成部分。POSIX 与 IEEE 1003 和 2003 家族的标准是可互换 的。除 1003.1 之外,1003 和 2003 家族也包括在表中。 管理 POSIX 开放式系统环境(OSE) 。IEEE 在 1995 年通过了这项标准。 ISO 的 1003.0 版本是 ISO/IEC 14252:1996。 被广泛接受、用于源代码级别的可移植性标准。1003.1 提供一个操作系统的 C 语 1003.1 言应用编程接口(API) 。IEEE 和 ISO 已经在 1990 年通过了这个标准,IEEE 在 1995 年重新修订了该标

4、准。 一个用于实时编程的标准(以前的 P1003.4 或 POSIX.4)。这个标准在 1993 年被 1003.1b IEEE 通过,被合并进 ISO/IEC 9945-1。 一个用于线程(在一个程序中当前被执行的代码段)的标准。以前是 P1993.4 或 1003.1c POSIX.4 的一部分,这个标准已经在 1995 年被 IEEE 通过,归入 ISO/IEC 9945-1:1996。 一个关于协议独立接口的标准,该接口可以使一个应用程序通过网络与另一个应用 1003.1g 程序通讯。 1996 年,IEEE 通过了这个标准。 一个应用于 shell 和工具软件的标准,它们分别是操作系

5、统所必须提供的命令处 1003.2 理器和工具程序。 1992 年 IEEE 通过了这个标准。ISO 也已经通过了这个标准 (ISO/IEC 9945-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/IEC 14519:1999。 一个相当于 1003.

6、1q(协议独立接口)的 Ada 语言的 API。在 1998 年, IEEE 通 1003.5c 过了这个标准。ISO 也通过了这个标准。 一个相当于 1003.1 的 FORTRAN 语言的 API。在 1992 年,IEEE 通过了这个标准, 1003.9 并于 1997 年对其再次确认。ISO 也已经通过了这个标准。 一个应用于超级计算应用环境框架(Application Environment Profile,AEP)的 1003.10 标准。在 1995 年,IEEE 通过了这个标准。 一个关于应用环境框架的标准,主要针对使用 POSIX 接口的实时应用程序。在 1003.13 19

7、98 年,IEEE 通过了这个标准。 1003.22 一个针对 POSIX 的关于安全性框架的指南。 一个针对用户组织的指南,主要是为了指导用户开发和使用支持操作需求的开放式 1003.23 系统环境(OSE)框架 针对指定和使用是否符合 POSIX 标准的测试方法,有关其定义、一般需求和指导 2003 方针的一个标准。在 1997 年,IEEE 通过了这个标准。 这个标准规定了针对 1003.1 的 POSIX 测试方法的提供商要提供的一些条件。在 2003.1 1992 年,IEEE 通过了这个标准。 一个定义了被用来检查与 IEEE 1003.2(shell 和 工具 API)是否符合的

8、测试方 2003.2 法的标准。在 1996 年,IEEE 通过了这个标准。 表 3.1 POSIX 标准的重要组成部分 本章将重点讲述“POSIX线程”,即符合 POSIX 国际正式标准 POSIXl003.1c-1995 的部分。 本章假定用户使用的编程语言为 ANSI C 语言。 3.2 POSIX 线程库 首先,在编写 POSIX 多线程 C 程序时,需要包含头文件pthread.h。POSIX 线程函数都 以pthread_开头。在本章中,我们将介绍一下线程操作函数: POSIX 函数 描述 pthread_cancel 终止另一个线程pthread_create 创建一个线程 pt

9、hread_detach 设置线程以释放资源 pthread_equal 测试两个线程 ID 是否相等pthread_exit 退出线程,而不退出进程2 pthread_join 等待一个线程 pthread_self 找出自己的线程 ID 表 3.2 POSIX 线程管理函数 3.2.1 创建线程 pthread_create 函数创建一个线程。int pthread_createpthread_t *restrict thread, const pthread_attr_t* restrict attr, void *start_routinevoid *, void *restrict

10、arg; 参数 thread 指向保存线程 ID 的 pthread_t 结构。参数 attr 表示一个封装了线程的各种属 性的属性对象,用来配置线程的运行,如果为 NULL,则使新线程具有默认的属性。线程属 性将在后面的 XX 节讨论。第三个参数 start_routine 是线程开始执行的时候调用的函数的 名字。这个函数必须具有以下的格式: void* start_routinevoid* arg; 返回的 void指针将被 pthread_join 函数当做退出状态来处理。第四个参数 arg 正是传递给 start_routine 函数的参数。POSIX 的pthread_create

11、函数会使创建的线程自动处于可运行 状态,而不需要一个单独的启动操作。 如果成功,pthread_create 返回 0,如果不成功,pthread_create 返回一个非零的错误码。 下表列出了 pthread_create 的错误形式及相应的错误码 错误 原因EAGAIN 系统没有创建线程所需的资源,或者创建线 程会超出系统对一个进程中线程总数的限制 EINVAL attr 参数是无效的 EPERM 调用程序没有适当的权限来设定调度策略或 attr 指定的参数 表 3.3 pthread_create的错误形式及相应的错误码 每一个线程可以通过调用函数 pthread_self 得到本线程

12、的 ID(数据结构类型:pthread_t) , 它的形式为: pthread_t pthread_selfvoid; 由于 pthread_t 可能是一个结构,因此 POSIX 提供了一个函数 pthread_equal 来比较线程 ID 是否相等。这个函数的形式为: int pthread_equalpthread_t t1, pthread_t t2;3 两个参数 t1和 t2 是两个线程 ID,如果它们相等,pthread_equal 就返回一个非零值,如果 不相等,则返回 0。 3.2.2 分离(Detach)和接合(Join)线程 POSIX 线程的一个特点是:除非线程是被分离了的

13、,否则在线程退出时,它的资源是不会被 释放的。pthread_detach函数用来分离线程: int pthread_detachpthread_t thread; 它设置线程的内部选项来说明线程退出后,其所占有的资源可以被回收。参数 thread 是要 分离的线程的 ID。被分离的的线程退出时不会报告它们的状态。如果函数调用成功, pthread_detach 返回 0,如果不成功,pthread_detach 返回一个非零的错误码。下表列出 了 pthread_detach 的错误形式及相应的错误码 错误 原因 EINVAL thread 对应的不是一个可分离的线程ESRCH 没有 ID

14、为 thread 的线程 表 3.4 pthread_detach的错误形式及相应的错误码 pthread_join 函数可以使调用这个函数的线程等待指定的线程运行完成再继续执行。它的 形式为: int pthread_joinpthread_t thread, void *value_ptr; 参数 thread为要等待的线程的 ID,参数 value_ptr 为指向返回值的指针提供一个位置,这 个返回值是由目标线程传递给 pthread_exit 或 return 的。如果 value_ptr 为 NULL,调用 程序就不会对目标线程的返回状态进行检索了。如果函数调用成功,pthread_

15、join返回 0, 如果不成功,pthread_join 返回一个非零的错误码。下表列出了 pthread_join 的错误形式 及相应的错误码 错误 原因EINVAL thread 对应的不是一个可接合的线程 ESRCH 没有 ID 为 thread 的线程 表 3.5 pthread_join 的错误形式及相应的错误码 如果线程没有被分离,并且执行 pthread_joinpthread_self,那么该线程将被一直挂 起,因为这条语句造成了死锁。有些 POSIX 的实现可以检测到死锁,并迫使 pthread_join 带着错误 EDEADLK 返回,但是,POSIX并不要求一定要进行这种

16、检测。4 3.2.3 退出和取消线程 进程的终止可以通过在主函数 main中直接调用 exit、return、或者通过进程中的任何其 它线程调用 exit 来实现。在任何一种情况下,该进程的所有线程都会终止。如果主线程在 创建了其它线程之后没有工作可做,它就应该阻塞到所有线程都结束为止,或者应该调用 pthread_exitNULL。 有时程序不必等待线程执行完成,这时程序需要使线程中途退出。POSIX 线程库提供了两个 撤销线程的函数 pthread_exit 和pthread_cancel。下面对这两个函数分别进行介绍。 pthread_exit 函数可以使调用这个函数的线程中止运行,并且

17、允许线程传递一个指针,这 个指针可以用来指向线程的返回值。它的形式为: void pthread_exitvoid *value_ptr; 连接了这个线程可以获得参数 value_ptr 的值。回顾前面介绍的 pthread_join 函数,这个 函数的参数 void *value_ptr,正是保存 pthread_exit 函数的参数 void *value_ptr 的地 址。这里要注意,pthread_exit 的参数 value_ptr 必须指向线程退出后仍然存在的数据。 POSIX 没有为 pthread_exit 定义任何错误。 POSIX doesnt define any err

18、or code for pthread_exit 线程也可以通过取消机制迫使其它的线程退出。线程可以调用函数 pthread_cancel 来请求 取消另一个线程。这个函数的形式是: int pthread_cancelpthread_t thread; 参数 thread是要取消的目标线程的线程 ID。该函数并不阻塞调用线程,它发出取消请求后 就返回了。如果成功,pthread_cancel 返回 0,如果不成功,pthread_cancel 返回一个非 零的错误码。 线程收到一个取消请求时会发生什么情况取决于它的状态和类型。如果线程处于 PTHREAD_CANCEL_ENABLE状态,它就

19、接受取消请求,如果线程处于 PTHREAD_CANCEL_DISABLE 状态,取消请求就会被保持在挂起状态。默认情况下,线程处于 PTHREAD_CANCEL_ENABLE 状态。 pthread_setcancelstate函数用来改变调用线程的取消状态,它的形式为: int pthread_setcancelstateint state, int *oldstate; 参数 state 表示要设置的新状态,参数 oldstate 为一个指向整形的指针,用于保存线程以 前的状态。如果成功,该函数返回 0,如果不成功,它返回一个非 0 的错误码。通常情况下, 线程函数在改变了线程的取消状态之

20、后,应该在执行完某些操作之后恢复线程的取消状态, 5否则,对于其它可能取消该线程的线程而言,取消操作的结果将无法预测,这很可能不利于 程序的正确执行。 当线程将退出作为对取消请求的响应时,取消类型允许线程控制它在什么地方退出。当它的 取消类型为 PTHREAD_CANCEL_ASYNCHRONOUS 时,线程在任何时候都可以响应取消请求。当它 的取消类型为 PTHREAD_CANCEL_DEFERRED 时,线程只能在特定的几个取消点上响应取消请 求。在默认情况下,线程的类型为 PTHREAD_CANCEL_DEFERRED。 pthread_setcanceltype函数用来修改线程的取消类

21、型。它的形式为: int pthread_setcanceltypeint type, int *oldtype; 参数 type 指定线程的取消类型,参数 oldtype 用来指定保存原来的取消类型的地址。如果 成功,该函数返回 0,如果不成功,它返回一个非 0 的错误码。 线程可以通过调用 pthread_testcancel 在代码中的特定的位置上设置一个取消点。当类型 为 PTHREAD_CANCEL_DEFERRED 的线程到达这样一个取消点时,就接受挂起的取消请求。该函 数的形式为: void pthread_testcancelvoid; 3.2.4 用户级线程与内核级线程 用户

22、级线程user-level thread和内核级线程kernel-level thread是两种传统的线程控 制模式。用户级线程通常都运行在一个现存的操作系统之上。这些线程对内核来说是不可见 的,它们被封装在进程里,并竞争分配给进程的资源。线程由一个线程运行系统来调度,这 个系统是进程代码的一部分。带有用户级线程的程序通常会连接到一个特殊的库上去,这个 库中的每个库函数都用外套jacket包装起来。在调用被外套包装的库函数之前,外套函数 要调用线程运行系统来进行线程管理,在调用了被外套包装的库函数之后,外套函数可能也 要进行这样的操作。 这样做的必要性是为了解决下面的情况:由于 read或 s

23、leep这样的函数可能会使进程阻塞, 所以它们给用户级线程带来了一个问题,那就是要避免某个线程在调用这些阻塞型函数之 后,整个进程被阻塞。这就要求用户级线程库用一个无阻塞的版本来替换每一个外套包装的、 潜在的阻塞型调用。线程运行系统通过测试来查看调用是否会使线程阻塞,如果调用不会阻 塞,运行系统就立即进行调用,但是,如果调用会阻塞,运行系统就会将线程放在一个等待 线程的列表中,将调用添加到一个动作列表中,以便稍后再试,然后挑选另一个线程来运行。 所有这些控制过程对用户和操作系统来说都是不可见的。 用户级线程的开销很低,但是它们也有些缺点。用户线程模型假定线程运行系统昀终会重新 获得控制权,这可

24、能会受到 CPU 绑定线程CPU-bound thread的阻碍。CPU 绑定线程很少执 行库函数调用,这样就会阻止线程运行系统重新获得控制权来调度其它的线程。程序员必须 要显式地迫使 CPU 绑定线程在适当的地方放弃对 CPU 的控制,以避免出现封锁状态。第二个 问题是,用户级线程只能共享分配给它们的封装进程的处理器资源。因为线程一次只能运行 6 在一个处理器上,这种约束限制了可用的并行总量。使用线程的主要原因之一就是要利用多 处理器工作站的优势,所以仅使用用户级线程本身并不是一种能让人接受的方法。 对内核级线程来说,内核了解每一个作为可调度实体的线程,这些线程可以在全系统范围内 竞争处理器

25、资源。内核级线程的调度开销可能和进程自身的调度差不多昂贵,但是,内核级 线程可以利用多处理器的优势。内核级线程的同步和数据共享比整个进程的同步和数据共享 的开销要低一些,但内核级线程的管理比用户级线程的管理代价更高。 还有一种模型,称作混合线程模型hybrid thread model,它通过提供两个级别的控制, 同时具备了用户级和内核级模型的优点。用户用用户级线程编写程序,然后说明有多少个内 核可调度实体与这个进程相关。运行时,将用户级线程映射为系统的可调度实体,以实现并 行。用户拥有的映射控制级别取决于实现,例如在 Sun 的Solaris 线程实现中,用户级线程 被称为线程,而内核可调度

26、实体被称为轻量级进程lightweight process。用户可以指定 由一个特定的轻量级进程来运行制定的线程,或者由一个轻量级进程池来运行一组制定的线 程。 POSIX 线程调度模型是一个混合模型,它很灵活,足以在标准的特定实现中支持用户级和内 核级的线程。模型中包括两级调度?线程级和内核实体级。线程与用户级线程类似,内核 实体由内核调度。由线程库来决定它需要多少内核实体,以及它们是如何映射的。 POSIX 引入了一个线程调度竞争范围thread-scheduling contention scope的概念,这个 概念赋予了程序员一些控制权,使它们可以控制怎样将内核实体映射为线程。线程的

27、contentionscope 属性可以是 PTHREAD_SCOPE_PROCESS,也可以是 PTHREAD_SCOPE_SYSTEM。 带有 PTHREAD_SCOPE_PROCESS 属性的线程与它们所在的进程中的其它线程竞争处理器资源。 POSIX 没有说明这样一个线程怎样与它所在的进程中的其它线程竞争,因此 PTHREAD_SCOPE_PROCESS线程可以是严格的用户级线程,或者它们也可以使用某种更复杂的 方式映射到一个内核实体池中去。带有 PTHREAD_SCOPE_SYSTEM属性的线程很像内核级线程, 他们在全系统范围内竞争处理器资源。POSIX 将 PTHREAD_SCO

28、PE_SYSTEM 线程和内核实体之 间的映射留给具体实现来完成,但是一种明显的映射方式是,将这样一个线程直接与内核实 体绑定起来。 POSIX 线程的具体实现可能支持 PTHREAD_SCOPE_PROCESS、或 PTHREAD_SCOPE_SYSTEM 或者两者都支持。 3.2.5 线程的属性 POSIX 将栈的大小和调度策略这样的特征封装到一个 pthread_attr_t 类型的对象中去,用 面向对象的方式表示和设置特征。属性对象只在线程创建的时候会对线程产生影响。编写程 序时可以先创建一个属性对象,然后再将栈的大小和调度策略这样的特征与属性对象关联起 来,之后就可以通过向 pthr

29、ead_create 传递相同的线程属性对象来创建多个具有相同特征 的线程。通过将各种特征组合到单个对象中去,POSIX 避免了用大量参数来调用 pthread_create 的情况。 表 3.6 显示的是线程属性的可设置特征及其相关函数,后面我们将对这些特征和函数进行讨 论。 7特征 函数 属性对象 pthread_attr_destroy pthread_attr_init 状态 pthread_attr_getdetachstatepthread_attr_setdetachstate 栈 pthread_attr_getguardsizepthread_attr_setguardsiz

30、e 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_i

31、nit用默认值对一个线程属性对象进行初始化。pthread_attr_destroy 函数将属性对象的值设为无效的。被设为无效的属性对象可以再次被初始化为一个新的属性 对象。pthread_attr_init 和 pthread_attr_destroy 都只有一个参数,即一个指向属性对 象的指针。这两个函数的形式为: int pthread_attr_initpthread_attr_t *attr; int pthread_attr_destroypthread_attr_t *attr; 如果成功,函数返回 0,如果不成功,函数返回一个非 0 的错误码。 大多数针对属性对象的函数都是获取或设置属性对象的属性。第一个参数是一个指向属性对 象的指针。对于获取操作,第二个参数是一个指向存放值的位置的指针,而对于设置操作, 第二个参数是属性的设置值。因此,后面读者可以根据函数参数的名称和类型推断出参数的 含义,我们就不一一介绍了。 3.2.5.1 线程状态线程状态的可能取值为 PTH

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

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