




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、2021-10-16Page 1 线程(thread)是在共享内存空间中并发的多道执行路径,它们共享一个进程的资源,如文件描述和信号处理。在两个普通进程(非线程)间进行切换时,内核准备从一个进程的上下文切换到另一个进程的上下文要花费很大的开销。和进程相比,线程的最大优点之一是数据的共享性,各个进程共享父进程处沿袭的数据段,可以方便的获得、修改数据。在串行程序基础上引入线程和进程是为了提高程序的并发度,从而提高程序运行效率和响应时间。2021-10-16Page 2 线程控制线程控制12.1创建线程创建线程12.2获取获取/ /设置线程属性设置线程属性12.3取消线程取消线程12.42021-1
2、0-16Page 3等待线程等待线程12.6信号量信号量12.7互斥锁互斥锁12.8线程的私有数据线程的私有数据12.52021-10-16Page 4 本章小结本章小结12.10思考与练习思考与练习12.11条件变量条件变量12.92021-10-16Page 5 线程是进程中的一个独立控制流,由环境(包括寄存器集和程序计数器)和一系列要执行的指令组成。所有进程至少由一个线程组成。多线程进程包括多个线程。所有线程共享为该进程分配的公共地址空间。使用 POSIX pthread API 函数可创建和处理用户线程。内核线程是一个内核可调度实体,它可以支持一个或多个用户线程。 从逻辑角度来看,多线
3、程的意义是从逻辑角度来看,多线程的意义是在一个应用程序中,有在一个应用程序中,有 多多 个个 执执 行行 部部 分分同时执行。但操作系统并没有将多个线同时执行。但操作系统并没有将多个线程看做多个独立的应用来实现进程的调程看做多个独立的应用来实现进程的调度、管理以及资源分配。一个线程可以度、管理以及资源分配。一个线程可以创建和撤销另一个线程,同一个进程中创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行的多个线程之间可以并发执行 2021-10-16Page 6 进程和线程最大的区别在于资源共享上,进程和线程最大的区别在于资源共享上,每个独立的线程有一个程序运行的入口、顺序每个独立的线
4、程有一个程序运行的入口、顺序执行序列和程序的出口。但是,线程不能够独执行序列和程序的出口。但是,线程不能够独立执行,必须依存在应用程序中,由应用程序立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。线程自己基本上不拥提供多个线程执行控制。线程自己基本上不拥有系统资源,只拥有少量在运行中必不可少的有系统资源,只拥有少量在运行中必不可少的资源(如程序计数器、一组寄存器、栈、线程资源(如程序计数器、一组寄存器、栈、线程说明信号掩码、局部线程变量和线程说明数据说明信号掩码、局部线程变量和线程说明数据),但是它可与同属一个进程的其他线程共享),但是它可与同属一个进程的其他线程共享进程所拥有的
5、全部资源(同一地址空间、通用进程所拥有的全部资源(同一地址空间、通用的信号处理机制、数据与的信号处理机制、数据与 I/O)。)。2021-10-16Page 7 线程是一些相关指令的离散序列,线程与线程是一些相关指令的离散序列,线程与其它指令序列的执行相互独立,每个程序至少其它指令序列的执行相互独立,每个程序至少包括一个线程,即主线程。主线程负责程序的包括一个线程,即主线程。主线程负责程序的初始化工作,并且执行初始指令。此后主线程初始化工作,并且执行初始指令。此后主线程会为执行各种不同的任务决定是分别创建其它会为执行各种不同的任务决定是分别创建其它线程还是由主线程独立承担。不管哪种情况,线程还
6、是由主线程独立承担。不管哪种情况,每个程序至少都包含一个线程,并且每个线程每个程序至少都包含一个线程,并且每个线程都会维护自己当前的机器状态。目前线程有用都会维护自己当前的机器状态。目前线程有用户线程和内核线程两种方法实现。户线程和内核线程两种方法实现。2021-10-16Page 81、用户线程、用户线程 用户线程在用户空间中实现,允许多线程的程用户线程在用户空间中实现,允许多线程的程序运行时不需要特定的内核支持,内核不需要直接序运行时不需要特定的内核支持,内核不需要直接对用户线程进程调度,内核的调度对象和传统进程对用户线程进程调度,内核的调度对象和传统进程一样,还是进程本身,内核并不知道用
7、户线程的存一样,还是进程本身,内核并不知道用户线程的存在。在。 由于由于Linux内核没有轻量级进程(线程)的概念内核没有轻量级进程(线程)的概念,因此不能独立地对用户线程进行调度,而是由一,因此不能独立地对用户线程进行调度,而是由一个线程运行库来组织线程的调度,其主要工作在于个线程运行库来组织线程的调度,其主要工作在于在各个线程的栈之间调度。如果一个进程中的某一在各个线程的栈之间调度。如果一个进程中的某一个线程调用了一个阻塞的系统调用,整个进程就会个线程调用了一个阻塞的系统调用,整个进程就会被调度程序切换为等待状态,其他线程得不到运行被调度程序切换为等待状态,其他线程得不到运行的机会。因此的
8、机会。因此linux使用了异步使用了异步I/O机制。机制。 2021-10-16Page 91、用户线程、用户线程 用户线程的优点如下:用户线程的优点如下: 某些线程操作的系统消耗大大减少。比如,对某些线程操作的系统消耗大大减少。比如,对属于同一个进程的线程之间进行调度切换时,属于同一个进程的线程之间进行调度切换时,不需要调用系统调用,因此将减少额外的消耗不需要调用系统调用,因此将减少额外的消耗,一个进程往往可以启动上千个线程。,一个进程往往可以启动上千个线程。 用户线程的实现方式可以被定制或修改,用户线程的实现方式可以被定制或修改,以适应特殊应用的要求。它对于多媒体实时过以适应特殊应用的要求
9、。它对于多媒体实时过程等尤其有用。另外,用户线程可以比内核线程等尤其有用。另外,用户线程可以比内核线程实现方法默认情况支持更多的线程。程实现方法默认情况支持更多的线程。 2021-10-16Page 102、内核线程、内核线程 Linux内核可以看作一个服务进程内核可以看作一个服务进程(管理软硬件资源,响应管理软硬件资源,响应用户进程的种种合理以及不合理的请求用户进程的种种合理以及不合理的请求)。内核需要多个执行流。内核需要多个执行流并行,为了防止可能的阻塞,多线程化是必要的。内核线程就并行,为了防止可能的阻塞,多线程化是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。是内核的分
10、身,一个分身可以处理一件特定事情。Linux内核内核使用内核线程来将内核分成几个功能模块,像使用内核线程来将内核分成几个功能模块,像kswapd、kflushd等,这在处理异步事件如异步等,这在处理异步事件如异步IO时特别有用。内核线时特别有用。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫做多线程内核时保存寄存器的空间。支持多线程的内核叫做多线程内核(Multi-Threads kernel )。内核线程的调度由内核负责,一个内。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响
11、其他的内核线程,因为其是调度核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。的基本单位。这与用户线程是不一样的。2021-10-16Page 112、内核线程、内核线程 Linux内核可以看作一个服务进程内核可以看作一个服务进程(管理软硬件资源,响应管理软硬件资源,响应用户进程的种种合理以及不合理的请求用户进程的种种合理以及不合理的请求)。内核需要多个执行流。内核需要多个执行流并行,为了防止可能的阻塞,多线程化是必要的。内核线程就并行,为了防止可能的阻塞,多线程化是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。是内核的分身,一个分身可以
12、处理一件特定事情。Linux内核内核使用内核线程来将内核分成几个功能模块,像使用内核线程来将内核分成几个功能模块,像kswapd、kflushd等,这在处理异步事件如异步等,这在处理异步事件如异步IO时特别有用。内核线时特别有用。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫做多线程内核时保存寄存器的空间。支持多线程的内核叫做多线程内核(Multi-Threads kernel )。内核线程的调度由内核负责,一个内。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,
13、因为其是调度核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。的基本单位。这与用户线程是不一样的。2021-10-16Page 122、内核线程、内核线程 Linux内核可以看作一个服务进程内核可以看作一个服务进程(管理软硬件资源,响应管理软硬件资源,响应用户进程的种种合理以及不合理的请求用户进程的种种合理以及不合理的请求)。内核需要多个执行流。内核需要多个执行流并行,为了防止可能的阻塞,多线程化是必要的。内核线程就并行,为了防止可能的阻塞,多线程化是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。是内核的分身,一个分身可以处理一件特定事情
14、。Linux内核内核使用内核线程来将内核分成几个功能模块,像使用内核线程来将内核分成几个功能模块,像kswapd、kflushd等,这在处理异步事件如异步等,这在处理异步事件如异步IO时特别有用。内核线时特别有用。内核线程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换程的使用是廉价的,唯一使用的资源就是内核栈和上下文切换时保存寄存器的空间。支持多线程的内核叫做多线程内核时保存寄存器的空间。支持多线程的内核叫做多线程内核(Multi-Threads kernel )。内核线程的调度由内核负责,一个内。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度核线
15、程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。的基本单位。这与用户线程是不一样的。2021-10-16Page 132021-10-16Page 14线程创建函数 pthread_create() 用来创建一个新的线程。其函数原型如下所示:#include int pthread_create (pthread_t *_restrict _newthread, _const pthread_attr_t *_restrict _attr, void *(*_start_routine) (void *), void *_restrict _arg) _T
16、HROW; 此函数第一个参数为指向线程标识符的指针,_restrict 是在 C99 中定义的新标准关键字,将视其修饰的变量不与其他变量关联,主要用来提高编译效率。 如果创建成功,第一个参数如果创建成功,第一个参数_newthread 将指向将指向一个该进程内唯一的线程标识值(线程一个该进程内唯一的线程标识值(线程ID),), 如如 果设果设置为置为 NULL,则不会返回生成的线程的标识值。关于,则不会返回生成的线程的标识值。关于当前线程的当前线程的 ID 值还可以通过值还可以通过 pthread_self() 函数获得函数获得,其声明如下:,其声明如下:extern pthread_t pt
17、hread_self (void) _THROW _attribute_ (_const_); 第二个参数用来设置线程属性,第二个参数用来设置线程属性,pthread_attr_t 是对动态分配的属性结构的一个引用。是对动态分配的属性结构的一个引用。 第三个参数是第三个参数是线程运行函数的起始地址,即在此线程中运行哪段代线程运行函数的起始地址,即在此线程中运行哪段代码,其返回值类型为一个码,其返回值类型为一个 void 类型的指针,如果不是类型的指针,如果不是 void 类型,可以使用类型转换类型,可以使用类型转换(void*(*()传递函数指传递函数指针以免编译错误。针以免编译错误。 202
18、1-10-16Page 15 第四个参数是运行函数的参数。如果需要第四个参数是运行函数的参数。如果需要给自定义的函数传入多个参数,则需要使用一给自定义的函数传入多个参数,则需要使用一个包含这些参数的结构,这个结构必须是静态个包含这些参数的结构,这个结构必须是静态分配并被初始化了的。分配并被初始化了的。 如果线程创建成功,它将拥有自己的线程如果线程创建成功,它将拥有自己的线程属性和执行栈,并从调用程序那里继承信号掩属性和执行栈,并从调用程序那里继承信号掩码和调试优先级。线程创建成功后,它将返回码和调试优先级。线程创建成功后,它将返回 0 并将并将_newthread 设置为指向该进程内的一设置为
19、指向该进程内的一个唯一个唯一 ID。如果失败,将返回非。如果失败,将返回非 0 值。值。2021-10-16Page 16 在在Linux中,采用的是中,采用的是“一对一一对一”的线的线程机制。也就是一个用户线程对应一个内核程机制。也就是一个用户线程对应一个内核线程。绑定属性就是指一个用户线程固定地线程。绑定属性就是指一个用户线程固定地分配给一个内核线程,因为分配给一个内核线程,因为CPU时间片的调时间片的调度是面向内核线程(轻量级进程)的,因此度是面向内核线程(轻量级进程)的,因此具有绑定属性的线程可以保证在需要的时候具有绑定属性的线程可以保证在需要的时候总有一个内核线程与之对应,而与之对应
20、的总有一个内核线程与之对应,而与之对应的非绑定属性就是指用户线程和内核线程的关非绑定属性就是指用户线程和内核线程的关系不是始终固定的,而是由系统来分配。系不是始终固定的,而是由系统来分配。2021-10-16Page 17(1)绑定属性。)绑定属性。在在Linux中,采用的是中,采用的是“一对一一对一”的线程机制。的线程机制。也就是一个用户线程对应一个内核线程。绑定也就是一个用户线程对应一个内核线程。绑定属性就是指一个用户线程固定地分配给一个内属性就是指一个用户线程固定地分配给一个内核线程,因为核线程,因为CPU时间片的调度是面向内核线时间片的调度是面向内核线程(轻量级进程)的,因此具有绑定属
21、性的线程(轻量级进程)的,因此具有绑定属性的线程可以保证在需要的时候总有一个内核线程与程可以保证在需要的时候总有一个内核线程与之对应,而与之对应的非绑定属性就是指用户之对应,而与之对应的非绑定属性就是指用户线程和内核线程的关系不是始终固定的,而是线程和内核线程的关系不是始终固定的,而是由系统来分配。由系统来分配。2021-10-16Page 18(2)分离属性。)分离属性。 分离属性是决定线程以一个什么样的方式来终止分离属性是决定线程以一个什么样的方式来终止自己。在非分离情况下,当一个线程结束时,它多占自己。在非分离情况下,当一个线程结束时,它多占用的线程没有得到释放,也就是没有真正的终止,需
22、用的线程没有得到释放,也就是没有真正的终止,需要通过要通过pthread_join来释放资源。而在分离属性情况下来释放资源。而在分离属性情况下,一个线程结束时会立即释放它所占有的系统资源。,一个线程结束时会立即释放它所占有的系统资源。但这里有一点要注意的是,如果设置一个线程分离属但这里有一点要注意的是,如果设置一个线程分离属性,而这个线程又运行得非常快的话,那么它很可能性,而这个线程又运行得非常快的话,那么它很可能在在pthread_create函数返回之前就终止了线程函数的运函数返回之前就终止了线程函数的运行,它终止以后就很有可能将线程号和系统资源移交行,它终止以后就很有可能将线程号和系统资
23、源移交给其他的线程使用,这时调用给其他的线程使用,这时调用pthread_create()的线程的线程就得到错误的线程号。就得到错误的线程号。2021-10-16Page 191获取获取/设置设置 detachstate 属性属性 detachstate 属性即设置创建一个线程后,该线程是处于分离状态属性即设置创建一个线程后,该线程是处于分离状态还是可连接状态。其合法值包括:还是可连接状态。其合法值包括: PTHREAD_CREATE_DETACHED:此选项使得使用:此选项使得使用 attr 创建创建的所有线程处于分离状态。线程终止时,系统将自动回收与带有的所有线程处于分离状态。线程终止时,
24、系统将自动回收与带有此状态的线程相关联的资源。调用使用此属性创建的此状态的线程相关联的资源。调用使用此属性创建的 pthread_detach()或或 pthread_join()函数将导致错误。函数将导致错误。 PTHREAD_CREATE_JOINABLE:此选项使得使用:此选项使得使用 attr 创建的创建的所有线程处于可连接状态。线程终止时,不会回收与带有此状态所有线程处于可连接状态。线程终止时,不会回收与带有此状态的线程相关联的资源。要回收系统资源,应用程序必须调用使用的线程相关联的资源。要回收系统资源,应用程序必须调用使用此属性创建的线程的此属性创建的线程的 pthread_det
25、ach()或或 pthread_join()函数。函数。detachstate 的缺省值是的缺省值是 PTHREAD_CREATE_JOINABLE。2021-10-16Page 202获取获取/设置设置 guardsize 属性属性 guardsize 属性允许应用程序指定使用此属性对象创建的线属性允许应用程序指定使用此属性对象创建的线程的守护区大小。所指定的守护区大小的单位为字节。大多数系程的守护区大小。所指定的守护区大小的单位为字节。大多数系统将守护区大小向上舍入为系统可配置变量统将守护区大小向上舍入为系统可配置变量 PAGESIZE 的倍数的倍数。如果指定了零值,则不会创建守护区。为应
26、用程序提供了。如果指定了零值,则不会创建守护区。为应用程序提供了guardsize 属性出于以下两个原因:属性出于以下两个原因: (1)溢出保护可能会导致系统资源浪费。如果应用程序创建大量线溢出保护可能会导致系统资源浪费。如果应用程序创建大量线程,并且已知这些线程永远不会溢出其栈,则可以关闭溢出保护程,并且已知这些线程永远不会溢出其栈,则可以关闭溢出保护区。通过关闭溢出保护区,可以节省系统资源。区。通过关闭溢出保护区,可以节省系统资源。 (2)线程在栈上分配大型数据结构时,可能需要较大的溢出保护区线程在栈上分配大型数据结构时,可能需要较大的溢出保护区来检测栈溢出。来检测栈溢出。 guardsi
27、ze 的缺省值为的缺省值为 PAGESIZE 字节。字节。PAGESIZE 的实际值与的实际值与实现相关,并且不可以在所有实现上使用相同值。如果用户堆栈实现相关,并且不可以在所有实现上使用相同值。如果用户堆栈的存储不是由的存储不是由 pthread 库分配的,将忽略库分配的,将忽略 guardsize 属性。属性。2021-10-16Page 213获取获取/设置设置 schedparam 属性属性 与与 schedpolicy 属性关联的属性关联的 schedparam 属性的合属性的合法值因调度策略的不同而异。对于法值因调度策略的不同而异。对于SCHED_FIFO 和和 SCHED_RR
28、调度策略,只需要调度策略,只需要 schedparam属性的属性的 sched_priority 成员。可以通过成员。可以通过 sched_get_priority_max()和和 sched_get_priority_min()获取获取 sched_priority 的合法值范围。其他调度策略的的合法值范围。其他调度策略的 schedparam的必要内容是不确定的。的必要内容是不确定的。 pthread_attr_getschedparam()可以从线程属性对象可以从线程属性对象 attr 中检索中检索 schedparam属性并在属性并在 param参数中返回此参数中返回此值。值。2021
29、-10-16Page 224获取获取/设置设置 schedpolicy 属性属性 schedpolicy 属性允许通过此属性对象创建的线程使用特定的属性允许通过此属性对象创建的线程使用特定的调度策略。要使用此属性,必须将调度策略。要使用此属性,必须将 inheritsched 属性设置为属性设置为 PTHREAD_EXPLICIT_SCHED。 pthread_attr_getschedpolicy()可以从线程属性对象可以从线程属性对象 attr 中检索中检索 schedpolicy 属性并在属性并在 policy参数中返回此值。参数中返回此值。 int pthread_attr_getsc
30、hedpolicy (_const pthread_attr_t *_restrict _attr, int *_restrict _policy) _THROW; pthread_attr_setschedpolicy()用于设置已初始化属性对象用于设置已初始化属性对象 attr 中中的的 schedpolicy 属性。属性。schedpolicy 属性的新值将传递给此函数中属性的新值将传递给此函数中的的 policy 参数。参数。2021-10-16Page 235获取获取/设置设置 inheritsched 属性属性 inheritsched 属性选用是从创建线程中继承属性选用是从创建线
31、程中继承还是从此属性对象中获得调度策略及关联属性还是从此属性对象中获得调度策略及关联属性。inheritsched 属性的合法值包括:属性的合法值包括: PTHREAD_INHERIT_SCHED:此选项指:此选项指定从创建线程中继承调度策略及关联属性。如定从创建线程中继承调度策略及关联属性。如果使用果使用 attr 创建了线程,将忽略创建了线程,将忽略 attr 参数中的参数中的调度策略和关联属性。调度策略和关联属性。 PTHREAD_EXPLICIT_SCHED:此选项指定:此选项指定从此属性对象中获得已创建线程的调度策略及从此属性对象中获得已创建线程的调度策略及关联属性。关联属性。202
32、1-10-16Page 246获取获取/设置设置 contentionscope 属性属性 contentionscope 属性用来设置创建线程的争用范围属性用来设置创建线程的争用范围,其合法值包括:,其合法值包括: PTHREAD_SCOPE_SYSTEM:使用此竞争范围创建:使用此竞争范围创建的线程,将与系统中(以及同一调度域中)的其他线的线程,将与系统中(以及同一调度域中)的其他线程争用资源。此属性一般用于表示用户线程应该直接程争用资源。此属性一般用于表示用户线程应该直接绑定到内核调度实体。绑定到内核调度实体。 PTHREAD_SCOPE_PROCESS:使用此竞争范围创:使用此竞争范围
33、创建的线程,将直接与其进程内使用此调度争用范围创建的线程,将直接与其进程内使用此调度争用范围创建的其他线程争用资源。此属性一般用于表示不应绑建的其他线程争用资源。此属性一般用于表示不应绑定用户线程(不绑定到任何特定的内核调度的实体)定用户线程(不绑定到任何特定的内核调度的实体)。2021-10-16Page 257获取获取/设置设置 stack 地址属性地址属性 此属性选项指定创建的线程将要使用的堆栈基址此属性选项指定创建的线程将要使用的堆栈基址。用程序全面负责这些堆栈的分配、管理和取消分配。用程序全面负责这些堆栈的分配、管理和取消分配。存储分配的选项为。存储分配的选项为 malloc(3C)
34、、brk(2)和和 mmap(2)函数。如果使用此选项,则只能使用此属性对象创建函数。如果使用此选项,则只能使用此属性对象创建一个线程。如果创建了多个线程,它们将使用同一个一个线程。如果创建了多个线程,它们将使用同一个堆栈。堆栈。stackaddr 属性的缺省值为属性的缺省值为 NULL。如果线程用。如果线程用户堆栈的存储不是由库分配的(也就是说,户堆栈的存储不是由库分配的(也就是说,stackaddr 属性不是属性不是 NULL),将忽略),将忽略 guardsize 属性。属性。 pthread_attr_getstackaddr()可以从线程属性对象可以从线程属性对象 attr 中检索中
35、检索 stackaddr 属性并在属性并在 stackaddr参数中返回此值参数中返回此值。2021-10-16Page 268获取获取/设置设置 stack 大小属性大小属性 stacksize 定义使用此属性对象创建的线程的用户堆栈大小(以定义使用此属性对象创建的线程的用户堆栈大小(以字节为单位)。字节为单位)。 stacksize 属性的合法值包括:属性的合法值包括: PTHREAD_STACK_MIN:此选项指定使用此属性对象创建的线:此选项指定使用此属性对象创建的线程的用户堆栈大小将使用缺省堆栈大小。此值为某个线程所需的程的用户堆栈大小将使用缺省堆栈大小。此值为某个线程所需的最小堆栈
36、大小。但对于所有线程来说,这个最小值可能无法接受最小堆栈大小。但对于所有线程来说,这个最小值可能无法接受。 Stacksize:具体的大小。定义使用此属性对象创建的线程的用户:具体的大小。定义使用此属性对象创建的线程的用户堆栈大小的个体数值。此值必须大于或等于最小堆栈大小堆栈大小的个体数值。此值必须大于或等于最小堆栈大小 PTHREAD_STACK_MIN。 pthread_attr_getstacksize()可以从线程属性对象可以从线程属性对象 attr 检索检索stacksize 属性并在属性并在 stacksize 参数中返回此值。参数中返回此值。 2021-10-16Page 279
37、设置设置/获取获取 guardsize属性属性guardsize 属性用来警戒堆栈的大小。设置线程属性用来警戒堆栈的大小。设置线程的的 guardsize属性的函数声明如下:属性的函数声明如下:int pthread_attr_setguardsize(pthread_attr_t *attr,size_t guardsize);/*用于设置已初始化属性对象用于设置已初始化属性对象 attr 中的中的 guardsize 属性属性*/获取线程获取线程guardsize属性的函数声明如下:属性的函数声明如下:int pthread_attr_getguardsize(pthread_attr_t
38、 *_attr,size_t*guardsize);/*获取获取guardsize属性属性*/ 2021-10-16Page 282021-10-16Page 29 如果目标线程的可取消性状态为 PTHREAD_CANCEL_ASYNCHRONOUS,则可随时执行新的或未决的取消请求。如果目标线程的可取消性状态为 PTHREAD_CANCEL_DEFERRED,则在目标线程到达一个取消点之前,取消请求将一直处于未决状态。如果禁用了目标线程的可取消性状态,则可取消性状态是无关紧要的。如果启用了可取消性,则可取消性状态将会生效。执行取消时,将调用线程的取消清理处理程序。 Linux 对取消线程操作
39、包括如下函数:对取消线程操作包括如下函数:(1)pthread_testcancel(void)函数用来测试当前线程。此函数声明函数用来测试当前线程。此函数声明如下:如下:void pthread_testcancel (void); pthread_testcancel()函数根据调用线程检查任何未决取消请求。如果函数根据调用线程检查任何未决取消请求。如果取消请求未决,并且调用线程启用其取消状态,则将处理该取消取消请求未决,并且调用线程启用其取消状态,则将处理该取消请求。如果禁用调用线程的取消状态,则此函数将无效。请求。如果禁用调用线程的取消状态,则此函数将无效。(2)pthread_can
40、cel()用于取消执行线程。此函数声明如下:用于取消执行线程。此函数声明如下:int pthread_cancel (pthread_t _cancelthread); pthread_cancel()函数请求取消执行线程。它允许线程以受控方式终函数请求取消执行线程。它允许线程以受控方式终止进程中执行的任何线程。止进程中执行的任何线程。 2021-10-16Page 31 在多线程环境下,进程内的所有线程共享进程的数据空间,因此全局变量为所有线程共享,在程序设计中有时候需要保存线程自己的全局变量,这种特殊的变量仅在某个线程内部有效。线程私有数据采用了一种被称为一键多值的技术,即一个键对应多个值
41、。访问数据时都是通过键值来访问,好像是对一个键值访问。操作线程私有数据的函数主要有四个:操作线程私有数据的函数主要有四个:pthread_key_create()(创建(创建一个键),一个键),pthread_setspecific()(为一个键设置线程私有数据)(为一个键设置线程私有数据),pthread_getspecific()(从一个键读取线程私有数据),(从一个键读取线程私有数据),phread_key_delete()(删除一个键)。这些函数的声明如下:(删除一个键)。这些函数的声明如下:#include int pthread_key_create(pthread_key_t *
42、key,void (*destr_function) (void*);int pthread_setspecific(pthread_key_t key,const void *pointer);void * pthread_getspecific(pthread_key_t key);int phread_key_delete(pthread_key_t key);pthread_key_create():从:从Linux的的TSD池中分配一项,将其值赋给池中分配一项,将其值赋给key供以后访问使用,它的第供以后访问使用,它的第1个参数个参数key为指向键值的指针,第为指向键值的指针,第2个
43、参数为一个函数指针,如果指针不为空,则在线程退出时将以个参数为一个函数指针,如果指针不为空,则在线程退出时将以key所关联的数据为参数调用所关联的数据为参数调用destr_function(),释放分配的缓冲区,释放分配的缓冲区。pthread_setspecific():该函数将:该函数将pointer的值与的值与key相关联。用相关联。用pthread_setspecific为一个键指定新的线程数据时,线程必须先释为一个键指定新的线程数据时,线程必须先释放原有的线程数据以回收空间。放原有的线程数据以回收空间。pthread_getspecific():通过该函数得到与:通过该函数得到与ke
44、y相关联的数据。相关联的数据。phread_key_delete():该函数用来删除一个键,删除后,键所占用的:该函数用来删除一个键,删除后,键所占用的内存将被释放。需要注意的是,键占用的内存被释放,与该键关内存将被释放。需要注意的是,键占用的内存被释放,与该键关联的线程数据所占用的内存并不被释放。因此,线程数据的释放联的线程数据所占用的内存并不被释放。因此,线程数据的释放必须在释放键之前完成。必须在释放键之前完成。2021-10-16Page 34pthread_join()函数用来等待一个线程的结束,类似于进程的 wait() 函数。函数声明如下:int pthread_join (pth
45、read_t _th, void *_thread_return); 此函数将阻塞调用当前线程的进程,直到此线程退出。即这个函数是一个线程阻塞的函数,调用它的进程将一直等待到被等待的线程结束为止,当函数返回时,处于被等待状态的线程资源被收回。 关于等待指定线程结束,需要注意以下几点:关于等待指定线程结束,需要注意以下几点:(1)一个线程只能等待连接一个其他线程。)一个线程只能等待连接一个其他线程。(2)发布连接操作的线程不一定是初始线程。)发布连接操作的线程不一定是初始线程。 (3)如果有多个线程等待同一线程执行,只有一个线程)如果有多个线程等待同一线程执行,只有一个线程能够得到正确的状态信息
46、。能够得到正确的状态信息。(4)有竞争关系的线程间的连接操作将返回一个错误。)有竞争关系的线程间的连接操作将返回一个错误。(5)如果启动连接的线程被取消,则处于等待状态的线)如果启动连接的线程被取消,则处于等待状态的线程可以被其他线程等待。程可以被其他线程等待。(6)如果目标线程在执行)如果目标线程在执行 pthread_join() 前结束,则该前结束,则该调用将不会引起任何阻塞并立即返回。调用将不会引起任何阻塞并立即返回。(7)一个未被连接的非独立线程在线程结束前一直占用)一个未被连接的非独立线程在线程结束前一直占用资源,直到创建它的进程结束。资源,直到创建它的进程结束。2021-10-1
47、6Page 35 分离线程是等待的逆操作。分离线程是等待的逆操作。pthread_detach() 库函数以欲操作的线程库函数以欲操作的线程 ID 为为其唯一的参数。如果执行成功,将使该线程与其唯一的参数。如果执行成功,将使该线程与当前进程分离,使其成为一个独立的线程,并当前进程分离,使其成为一个独立的线程,并返回返回 0,当,当 thread终止时,系统将自动回收它终止时,系统将自动回收它的资源;如果执行失败,将不分离指定的线程的资源;如果执行失败,将不分离指定的线程并返回非零值。并返回非零值。pthread_detach() 函数声明如函数声明如下:下: int pthread_detac
48、h (pthread_t _th) _THROW; 2021-10-16Page 362021-10-16Page 371965 年,E.W.Dijkstra提出了信号量的概念,之后信号量成为操作系统实现互斥和同步的一种普遍机制。信号量是一种特殊的变量,只能取正整数值,对这些正整数只能采取两种操作,P操作(代表等待、关操作)、V操作(代表信号、开操作)。定义如下:P(sem):如果sem的值大于0,则sem减1,如果sem的值为0,则挂起该线程。V(sem):如果有其他进程因等待sem而被挂起,则让它恢复执行,如果没有线程等待sem而被挂起,则sem加上1。1、创建信号量、创建信号量 在使用信
49、号量之前,首先需要创建一个信号量。创建一个信号量在使用信号量之前,首先需要创建一个信号量。创建一个信号量的函数为的函数为 semget(),其函数声明如下:,其函数声明如下: #include #include #include int semget (key_t , int nsems, int flag); 该函数用来打开一个新的信号量集合,或者打开一个已经存在的该函数用来打开一个新的信号量集合,或者打开一个已经存在的信号量集合。其中,参数信号量集合。其中,参数key表示所创建或打开的信号量集的键表示所创建或打开的信号量集的键;参数;参数nsems表示创建的信号量集中信号量的个数,此参数只
50、在表示创建的信号量集中信号量的个数,此参数只在创建一个新的信号量集时才有效;参数创建一个新的信号量集时才有效;参数flag表示调用函数的操作表示调用函数的操作类型,也可以用来设置信号量集的访问权限。调用函数类型,也可以用来设置信号量集的访问权限。调用函数semget()的作用由参数的作用由参数key和和flag决定,函数调用成功时返回值为信号量的决定,函数调用成功时返回值为信号量的引用标识符,调用失败时,返回引用标识符,调用失败时,返回-1。 2021-10-16Page 382、对信号量的操作、对信号量的操作对信号量的操作使用如下函数:对信号量的操作使用如下函数:#include#inclu
51、de#includeint semop(int semid, struct sembuf semoparray, size_t nops); 参数参数semid是信号量集的引用是信号量集的引用id,semoparray是一个是一个sembuf类型数组,类型数组,sembuf结构用于指定调用结构用于指定调用semop()函数所作的操作,数组函数所作的操作,数组semoparray中中的元素的个数由的元素的个数由nops决定。决定。sembuf结构如下:结构如下:struct sembuf usbort sem_num; short sem_op; short sem_flag;2021-10-1
52、6Page 39其中,其中,sem_num指定要操作的信号量;指定要操作的信号量;sem_flag为操作标记;为操作标记;sem_op用于用于表示所执行的操作,相应取值的含义如下:表示所执行的操作,相应取值的含义如下:sem_op0:表示线程对资源使用完毕,交回该资源。此时信号量集的:表示线程对资源使用完毕,交回该资源。此时信号量集的semid_ds结构的结构的sem_base.semval将加上将加上sem_op的值。如果此时设置了的值。如果此时设置了SEM_UNDO位,则信号量的调整值将减去位,则信号量的调整值将减去sem_op绝对值。绝对值。sem_op=0:表示进程要等待,直到:表示进
53、程要等待,直到sem_base.semval的值变为的值变为0。sem_op0:表示进程希望使用资源。此时将比较:表示进程希望使用资源。此时将比较sem_base.semval和和sem_op的绝对值的大小。如果的绝对值的大小。如果sem_base.semval大于大于sem_op的绝对值,表示资源的绝对值,表示资源足够分配给该进程,足够分配给该进程,sem_base.semval将减去将减去sem_op的绝对值。此时如果的绝对值。此时如果设置了设置了SEM_UNDO位,则信号量的调整值将加上位,则信号量的调整值将加上sem_op的绝对值。如的绝对值。如果如果果如果sem_base.semva
54、l小于小于sem_op的绝对值,表示资源不足。如果设置的绝对值,表示资源不足。如果设置了了IPC_NOWAIT位,则函数出错返回,否则位,则函数出错返回,否则semid_ds结构的结构的sem_base.semncnt加加1,进程等待至,进程等待至sem_base.semval大于大于sem_op的绝对的绝对值或该信号量被删除。值或该信号量被删除。2021-10-16Page 403、对信号量的控制、对信号量的控制对信号量的控制操作是通过对信号量的控制操作是通过semctl()来山实现的,函来山实现的,函数原型如下:数原型如下:#include#include#includeint semct
55、l(int semid, int semnum, int cmd, union senum arg);其中其中semid为信号量集的引用标识符,为信号量集的引用标识符,semnum用于指用于指定信号量集中某个特定的信号量,参数定信号量集中某个特定的信号量,参数cmd表示调用该表示调用该函数希望执行的操作,参数函数希望执行的操作,参数arg是是semun联合。联合。 2021-10-16Page 41 cmd参数的最常用的两个值是参数的最常用的两个值是SETVAL和和IFC_RMID,SETVAL用来把信号量初始化为用来把信号量初始化为一个已知的值,这个值在一个已知的值,这个值在semnu结构里
56、是以结构里是以val成员的面貌传递的,作用是在信号量第一次使成员的面貌传递的,作用是在信号量第一次使用之前对其进行设置;用之前对其进行设置;IFC_RMID的作用是删的作用是删除一个已经没人使用的信号量标识码。除一个已经没人使用的信号量标识码。 semctl()会根据会根据cmd参数返回几个不同的值,对参数返回几个不同的值,对于于SETVAL和和IFC_RMID,成功时返回,成功时返回0,失,失败时返回败时返回-1。 2021-10-16Page 422021-10-16Page 43 互斥锁通过锁机制来实现线程间的同步,互斥锁从本质上说就是一把锁,提供对共享资源的保护访问。互斥锁具有以下三个
57、特性:原子性:如果一个线程锁定了一个互斥量,那么临界区内的操作要么全部完成,要么一个也不执行。函数功能pthread_mutex_init初始化一个互斥锁pthread_mutex_destroy注销一个互斥锁pthread_mutex_lock加锁,如果不成功,阻塞等待pthread_mutex_unlock解锁pthread_mutex_trylock测试加锁,如果不成功则立即返回2021-10-16Page 44 1.初始化初始化 在在Linux 下,线程的互斥锁在使用前,要对它进行初始化。对下,线程的互斥锁在使用前,要对它进行初始化。对于静态分配的互斥锁,可以把它设置为于静态分配的互斥
58、锁,可以把它设置为PTHREAD_MUTEX_INITIALIZER ,或者调用,或者调用pthread_mutex_init()。操作语句如下:。操作语句如下: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 对于动态分配的互斥量,在申请内存(对于动态分配的互斥量,在申请内存(malloc)之后,通过)之后,通过pthread_mutex_init()进行初始化,并且在释放内存(进行初始化,并且在释放内存(free)前)前需要调用需要调用pthread_mutex_destroy()来注销互斥锁。操作语句如来注销互斥锁。操作语句如下:下:
59、 int pthread_mutex_init(pthread_mutex_t *restrict mutex , const pthread_mutexattr_t*restric attr ); int pthread_mutex_destroy (pthread_mutex_t *mutex); 返回值:成功则返回返回值:成功则返回0 ,出错则返回错误编号。,出错则返回错误编号。 2021-10-16Page 45 表表12-2 互斥锁的属性互斥锁的属性 属性值意义PTHREAD_MUTEX_TIMED_NP普通锁,当一个线程加锁后,其余请求锁的线程形成等待队列,解锁后按优先级获得锁PT
60、HREAD_MUTEX_RECURSIVE_NP嵌套锁,允许一个线程对同一个锁多次加锁,并通过多次unlock解锁,如果不是同线程请求,则在解锁时重新竞争PTHREAD_MUTEX_ERRORCHECK_NP检错锁,解锁后重新竞争PTHREAD_MUTEX_ADAPTIVE_NP适应锁2021-10-16Page 462.互斥操作互斥操作 初始化以后就可以对互斥锁进行加锁,如果互斥锁已初始化以后就可以对互斥锁进行加锁,如果互斥锁已经上了锁,调用线程会阻塞,直到被解锁。首先说经上了锁,调用线程会阻塞,直到被解锁。首先说一下加锁函数。一下加锁函数。 原型:原型: int pthread_mutex
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 微型化X射线管阵列在电子显微镜中的热应力分布与寿命预测模型构建
- 循环经济背景下废旧钢丝粉体再生利用的拓扑缺陷修复机制
- 循环经济导向下硅胶板边角料制备生物降解复合材料的分子键重构策略
- 建筑隔震结构中减震胶层间摩擦系数的长期蠕变特性研究
- 分布式光储直流耦合系统集成方案
- 《走进纸的世界》(教学设计)吉美版三年级上册综合实践活动
- 2025年穴位敷贴培训试题及答案
- 初中数学真题及图形题及答案
- 认识太阳能热水器 (教学设计)2023-2024学年人教鄂教版科学五年级上册
- 6.2金属的化学性质说课稿-2025-2026学年九年级化学科粤版(2012)下册
- 2025北京京剧院招聘工作人员10人备考试题及答案解析
- 新教科版科学六年级上册全册表格式核心素养目标教案 (一)
- 小学道德与法治教师考试题及答案
- (2025年标准)管护移交协议书
- 中医药现代化国际市场拓展:2025年中医药国际市场竞争力提升策略报告
- 生殖道支原体感染诊治中国专家共识(2025年版)解读课件
- 2025-2026人教版(PEP)一年级上册英语教学计划 (三篇)
- OTC药品终端销售方案
- 医院宣传稿件写作方法
- 急腹症的基本临床实践指南解读
- 中药外敷的课件
评论
0/150
提交评论