线程

线程包含了表示进程内执行环境必需的信息,其中包括进程中标识线程的线程ID 一组寄存器值  栈  调度优先级和策略  信号屏蔽字  errno变量以及线程私有数据。进程的所有信息对该进程的所有线程都是共享的,包括可执行的程序文本  程序的全局内存和堆内存  栈以及文件描述符。以下所讨论的线程是基于posix标准的。
一 线程标识
       就像每个进程都有一个进程ID一样,每个线程也有一个线程ID。进程ID在整个系统中是唯一的,但线程ID不同,线程ID只在它所属的进程环境中有效。进程ID是用pid_t数据类型来表示,是一个非负整数。线程ID则用pthread_t数据类型来表示,实现的时候用一个结构来代表pthread_t数据类型,所以可移植的操作系统实现不能把它作为整数处理。
线程可以通过调用pthread_self函数获得自身的线程ID。
#include   <pthread.h>
pthread_t    pthread_self(void);
返回值:调用线程的线程ID。

对两个线程ID的比较:
#include   <pthread.h>
int   pthread_equal(pthread_t tid1, pthread_t tid2);
返回值:若相等则返回非0值,否则返回0值。

二  线程的创建与终止
线程创建
      #include  <pthread.h>
     int  pthread_create(pthread_t  * restrict  tidp, const  pthread_attr_t  *restrict attr, void  *(*start_rtn)                                             (void), void *restrict  arg);
     返回值:若成功则返回0,否则返回错误编号
     说明:
     1.当pthread_create成功返回时,由tidp指向的内存单元被设置为新创建线程的线程ID。attr参数用于定制各种不同的线程属性。当被设置为NULL时即为创建默认属性的线程。
     2.新创建的线程从start_rtn函数的地址开始运行,该函数只有一个无类型指针参数arg,如果需要向start_rtn函数传递的参数不止一个,那么需要把这些参数放到一个结构中,然后把这个结构的地址作为arg参数传入。
     3.线程创建时并不能保证哪个线程会先运行:是新创建的线程还是调用线程。新创建的线程可以访问进程的地址空间,并且继承调用线程的浮点环境和信号屏蔽字,但是该线程的未决信号集被清除。
     4.pthread函数在调用失败时通常会返回错误码,它们并不像其他的posix函数一样设置errno。每个线程都提供errno的副本,这只是为了与使用errno的现有函数兼容。在线程中,从函数中返回错误码更为清晰整洁,不需要依赖那些随着函数执行不断变化的全局状态,因而可以把错误的范围限制在引起出错的函数中。

线程终止
       如果进程中的任一线程调用了exit,_Exit或者_exit,那么整个进程就会终止,与此类似,如果信号的默认动作是终止进程,那么,把该信号发送到线程会终止整个进程。单个线程可以通过下列三种方式退出,在不终止整个进程的情况下停止它的控制流。
        1.线程只是从启动例程中返回,返回值是线程的退出码,其实也就是return返回。
        2.线程可以被同一进程中的其他线程取消。
        3.线程调用pthread_exit。
#include <pthread.h>
void    pthread_exit(void *rval_ptr);
rval_ptr是一个无类型指针,与传给启动例程的单个参数类似,进程中的其他线程可以通过调用pthread_join函数访问到这个指针。
#include <pthread.h>
int    pthread_join(pthread_t thread, void  **rval_ptr);
返回值:若成功则返回0,否则返回错误编号
说明:
1.调用线程将一直阻塞,直到指定的线程调用pthread_exit  从启动例程中返回或者被取消。
2.如果线程只是从它的启动例程返回,rval_ptr将包含返回码。如果线程被取消,由rval_ptr指定的内存单元就置为PTHREAD_CANCELED。
3.在默认情况下,线程的终止状态会保存到对该线程调用pthread_join,如果线程已经处于分离状态,线程的底层存储资源可以在线程终止时被内核立即收回。当线程被分离时,并不能用pthread_join函数等待它的终止状态。对分离状态的线程进行pthread_join的调用就会失败,返回EINVAL。pthread_detach调用可以用于使线程进入分离状态:
#include    <pthread.h>
int    pthread_detach(pthread_t    tid);
返回值:若成功则返回0,否则返回错误编号。
4.如果对线程的返回值并不感兴趣,可以把rval_ptr置为NULL。在这种情况下,调用pthread_join函数将等待指定的线程终止,但并不获取线程的终止状态。

线程可以通过调用pthread_cancel函数来请求取消同一进程中的其他线程。
#include <pthread.h>
int    pthread_cancel(pthread_t  tid);
返回值:若成功则返回0,否则返回错误编号
说明:在默认的情况下,pthread_cancel函数会使得由tid标识的线程的行为表现为如同调用了参数为PTHREAD_CANCELED的pthread_exit函数,但是,pthread_cancel并不等于线程终止,它仅仅是提出请求,线程可以选择忽略取消方式或控制取消方式。

线程处理程序
线程可以安排它退出时需要调用的函数,这与进程可以用atexit函数安排进程退出时需要调用的函数是类似的。这样的函数称为线程清理程序。线程可以建立多个清理处理程序,处理程序记录在栈中,也就是说它们的执行顺序与它们注册时的顺序相反。
#include <pthread.h>
void    pthread_cleanup_push(void (*rtn)(void *), void *arg);
void    pthread_cleanup_pop(int  execute);
说明:
1.当线程执行以下动作时调用清理函数,调用参数为arg,清理函数rtn的调用顺序是由pthread_cleanup_push函数来安排的。以下情况清理函数会被调用:
         调用pthread_exit时。
         响应取消请求时。
         用非零execute参数调用pthread_cleanup_pop时。
如果execute参数置为0,清理函数将不被调用。无论哪种情况,pthread_cleanup_pop都将删除上次pthread_cleanup_push调用建立的清理处理程序。
2.这些函数有一个限制,就是pthread_cleanup_push和pthread_cleanup_pop都必须成对出现,这也和这些函数是宏实现有关吧。
三  线程属性
可 以使用pthread_attr_t结构修改线程默认属性,并把这些属性与创建的线程联系起来。可以使用pthread_attr_init函数初始化 pthread_attr_t结构。调用pthread_attr_init以后,pthread_attr_t结构所包含的内容就是操作系统实现支持的 线程所有属性的默认值。如果要修改其中个别属性的值,需要调用其他的函数。
#include   <pthread.h>
int    pthread_attr_init(pthread_attr_t *attr);
int    pthread_attr_destroy(pthread_attr_t  *attr);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:
1. 如果要去除对pthread_attr_t结构的初始化,可以调用pthread_attr_destroy函数。pthread_attr_init实 现时为属性对象分配了动态内存空间,pthread_attr_destroy将会释放该内存空间。除此之外,pthread_attr_destroy 还会用无效的值初始化属性对象,因此如果该属性对象被误用,将会导致pthread_create函数返回错误。
2.pthread_attr_t结构对应用程序是不透明的,也就是说应用程序并不需要了解有关属性对象内部结构的任何细节,因而可以增强应用程序的可移植性。posix沿用了这种模型,并且为查询和设置每种属性定义了独立的函数。

 
分离属性
       如果在创建线程时就知道不需要了解线程的终止状态,则可以修改pthread_attr_t结构中的detachstate线程属性,让线程以分离状态启 动。可以使用pthread_attr_setdetachstate函数把线程属性detachstate设置为下面的两个合法值之一:设置为 PTHREAD_CREATE_DETACHED,以分离状态启动线程; 或者设置为PTHREAD_CREATE_JOINABLE,正常启动线程,应用程序可以获得线程的终止状态。
#include   <pthread.h>
int     pthread_attr_getdetachstate(const  pthread_attr_t    *restrict attr, int  * detachstate);
int     pthread_attr_setdetachstate( pthread_attr_t    *attr, int  * detachstate);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:可以调用pthread_attr_getdetachstate函数获取当前的detachstate线程属性,获取到的属性放在第二个参数中。

线程栈属性
线程栈属性的查询和修改一般是通过较新的函数pthread_attr_getstack和pthread_attr_setstack来进行。这些新的函数消除了老接口定义中存在的二义性。
#include  <pthread.h>
int     pthread_attr_getstack(const  pthread_attr_t   *restrict  attr, void  **restrict  stackaddr,  size_t                                                               *restrict  stacksize);
int     pthread_attr_setstack(const  pthread_attr_t   *attr, void  *stackaddr,  size_t                                                                                                 *stacksize);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:
1. 这两个函数可以用于管理stackaddr线程属性,也可以用于管理stacksize线程属性。
2. 对进程来说,虚拟地址空间的大小是固定的,进程中只有一个栈,所以它的大小通常不是问题。但对线程来说,同样大小的虚拟地址空间必须被所有的线程栈共享。 如果应用程序使用了太多的线程,致使线程栈的累计大小超过了可用的虚拟地址空间,这时就需要减少线程默认栈的大小。另一方面,如果线程调用的函数分配了大 量的自动变量或者调用的函数涉及很深的栈帧,那么这时需要的栈大小可能要比默认的大。
3. 如果用完了线程栈的虚拟地址空间,可以使用malloc或者mmap来为其他栈分配空间,并用pthread_attr_setstack函数来改变新建 线程的栈位置。线程栈所占内存范围中可寻址的最低地址可以由stackaddr参数指定,该地址与处理器结构相应的边界对齐。
4.stackaddr线程属性被定义为栈的内存单元的最低地址,但这并不必然是栈的开始位置。对于某些处理器结构来说,栈是从高地址向低地址方向伸展的,那么stackaddr线程属性就是栈的结尾而不是开始位置。

guardsize属性
线 程属性guardsize控制着线程栈末尾之后用以避免栈溢出的扩展内存的大小。这个属性默认设置为PAGESIZE个字节。可以把guardsize线 程属性设为0,从而不允许属性的这种特征行为发生:在这种情况下不会提供警戒缓冲区。同样地,如果对线程属性stackaddr作了修改,系统就会假设我 们会自己管理栈,并使警戒栈缓冲区机制无效,等同于把guardsize线程属性设为0.
#include   <pthread.h>
int     pthread_attr_getguardsize(const pthread_attr_t  *restrict  attr,  size_t  *restrict   guardsize);
int     pthread_attr_setguardsize(pthread_attr_t  *attr,  size_t   guardsize);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:如果guardsize线程属性被修改了,操作系统可能把它取为页大小的整数倍。如果线程的栈指针溢出到警戒区域,应用程序就可能通过信号接收到出错信息。
更多的线程属性
可取消状态
可取消类型
并发度
这些属性并没有在pthread_attr_t结构中表达。
     并发度控制着用户级线程可以映射的内核线程或进程的数目。如果操作系统的实现在内核级的线程和用户级的线程之间保持一对一的映射,那么改变并发度并不会有 什么效果,因为所有的用户级的线程都可能被调度到。但是,如果操作系统的实现让用户级线程到内核级线程或进程之间的映射关系是多对一的话,那么在给定时间 内增加可运行的用户级线程数,可能会该善性能。pthread_setconcurrency函数可以用于提示系统,表明希望的并发度。
#include    <pthread.h>
int     pthread_getconcurrency(void);
返回值:当前的并发度
int     pthread_setconcurrency(int  level);
返回值:若成功则返回0,否则返回错误编号
说明:
1. pthread_getconcurrency函数返回当前的并发度。如果操作系统当前正控制着并发度(即之前没有调用过pthread_setconcurrency函数),那么pthread_getconcurrency将返回0。
2. pthread_setconcurrency函数设定的并发度只是对系统的一个提示,系统并不保证请求的并发度一定会被采用。如果希望系统自己决定使用 什么样的并发度,就把传入的参数level设为0.这样,应用程序调用level参数为0的pthread_setconcurrency函数,就可以撤 销在这之前level参数非零的pthread_setconcurrency调用所产生的作用。

可取消状态和可取消类型属性影响着线程在响应pthread_cancel函数调用时所呈现的行为。
可取消状态属性可以是PTHREAD_CANCEL_ENABLE,也可以是PTHREAD_CANCEL_DISABLE。线程可以通过调用pthread_setcancelstate修改它的可取消状态。
#include    <pthread.h>
int    pthread_setcancelstate(int  state,  int   *oldstate);
返回值:若成功则返回0,否则返回错误编号
说明:
1.pthread_setcancelstate把当前的可取消状态设置为state,把原来的可取消状态存放在由oldstate指向的内存单元中,这两步是原子操作。
2.pthread_cancel调用并不等待线程终止,在默认情况下,线程在取消请求发出后还是继续运行,知道线程到达某个取消点。取消点是线程检查是否被取消并按照请求进行动作的一个位置。posix保证在调用以下列出任何函数时,取消点都会出现。
 
3. 线程启动时默认的可取消状态是PTHREAD_CANCEL_ENABLE。当状态设为PTHREAD_CANCEL_DISABLE时,对 pthread_cancel的调用并不会杀死线程,相反,取消请求对这个线程来说处于未决状态。当取消状态再次变为 PTHREAD_CANCEL_ENABLE时,线程将在下一个取消点上对所有未决的取消请求进行处理。

如果应用程序在很长一段时间内都不会调用到以上定义取消点的函数,那么可以调用pthread_testcancel函数在程序中自己添加取消点。
#include    <pthread.h>
void     pthread_testcancel(void);
说明:调用pthread_testcancel时,如果有某个取消请求正处于未决状态,而且取消并没有置为无效,那么线程就会被取消。但是如果取消被置为无效时,pthread_testcancel调用就没有任何效果。

这里所描述的默认取消类型也称为延迟取消,延迟取消是调用pthread_cancel以后,在线程到达取消点之前,并不会出现真正的取消。异步取消与延迟取消不同,使用异步取消时,线程可以在任意时间取消,而不是非得遇到取消点才能被取消。
可以通过调用pthread_setcanceltype来修改取消类型。
#include    <pthread.h>
int     pthread_setcanceltype(int  type,  int  *oldtype);
返回值:若成功则返回0,否则返回错误编号
说明:
pthread_setcanceltype函数把取消类型设置为type,把原来的取消类型返回到oldtype指向的整型单元。type参数可以是PTHREAD_CANCEL_DEFERRED,也可以是PTHREAD_CANCEL_ASYNCHRONOUS。

线程安全
线程私有数据

四  线程同步
1.互斥量
         可以通过使用pthread的互斥接口保护数据,确保同一时间只有一个线程访问数据。互斥量从本质上来说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所有在该互斥锁上的阻塞线程都会变成可运行状态,第一个变为可运行状态的线程可以对互斥量加锁,其他线程将会看到互斥锁依然被锁住,只能回去再次等待它重新变为可用。在这种方式下,每次只有一个线程可以向前执行。
         互斥变量用pthread_mutex_t数据类型来表示,在使用互斥变量以前,必须首先对它进行初始化,可以把它置为常量PTHREAD_MUTEX_INITIALIZER(静态分配互斥量),也可以通过调用pthread_mutex_init函数进行初始化(动态分配互斥量)。如果动态地分配互斥量,那么在释放内存前需要调用pthread_mutex_destroy。
#include    <pthread.h>
int     pthread_mutex_init(pthread_mutex_t    *restrict   mutex, const  pthread_mutexattr_t   *restrict  attr);
int     pthread_mutex_destroy(pthread_mutex_t  *mutex);
返回值:若成功则返回0,否则返回错误编号
说明:要用默认的属性初始化互斥量,只需把attr设置为NULL。

对互斥量进行加锁,需要调用pthread_mutex_lock,如果互斥量已经上锁,调用线程将阻塞直到互斥量被解锁。对互斥量解锁,需要调用pthread_mutex_unlock。
#include    <pthread.h>
int     pthread_mutex_lock(pthread_mutex_t          *mutex);
int     pthread_mutex_trylock(pthread_mutex_t     *mutex);
int     pthread_mutex_unlock(pthread_mutex_t      *mutex);
返回值:若成功则返回0,否则返回错误编号
说明:
1.如果线程不希望被阻塞,它可以使用pthread_mutex_trylock尝试对互斥量进行加锁。如果调用pthread_mutex_trylock时互斥量处于未锁住状态,那么pthread_mutex_trylock将锁住互斥量,不会出现阻塞并返回0,否则pthread_mutex_trylock就会失败,不能锁住互斥量,而返回EBUSY。
2.要注意的是避免死锁的问题,如果线程试图对同一个互斥量加锁两次,那么它自身就会陷入死锁状态,或者在使用多个互斥量时两个线程都在等待对方锁住的互斥量,然后就会陷入死锁状态,所以在设计程序时要注意死锁问题。
互斥量属性
用pthread_mutexattr_init初始化pthread_mutexattr_t结构,用pthread_mutexattr_destroy来对该结构进行回收。
#include   <pthread.h>
int    pthread_mutexattr_init(pthread_mutexattr_t  *attr);
int    pthread_mutexattr_destroy(pthread_mutexattr_t  *attr);
返回值:若成功则返回0,否则返回错误编号
说明:
1.类型互斥量属性控制着互斥量的特性,POSIX.1定义了四种类型:
标准互斥量类型:PTHREAD_MUTEX_NORMAL(阻塞  并不做任何特殊的错误检查或死锁检测)
递归互斥量类型:PTHREAD_MUTEX_RECURSIVE(允许同一线程在互斥量解锁之前对互斥量进行多次加锁,用一个递                                                                                            归互斥量维护锁的计数,在解锁的次数和加锁次数不相同的情况下                                                                                             不会释放锁)
检错互斥量类型:PTHREAD_MUTEX_ERRORCHECK(不阻塞   提供错误检查)
默认互斥量类型:PTHREAD_MUTEX_DEFAULT(一般是指上面的标准类型,不同操作系统映射到上面三种锁类型不一                                                                                       样)
可以用pthread_mutexattr_gettype函数得到互斥量类型属性,用pthread_mutexattr_settype函数修改互斥量类型属性。
#include    <pthread.h>
int    pthread_mutexattr_gettype(const  pthread_mutexattr_t  *restrict  attr,   int  *restrict  type);
int    pthread_mutexattr_settype(pthread_mutexattr_t  *attr,   int  type);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:如果需要把现有的单线程接口放到多线程环境中,递归互斥量是非常有用的,但递归锁的使用需要一定的技巧,所以只应在没有其他可行的方案情况下使用。
进程共享属性:存在这样的机制,允许相互独立的多个进程把同一个内存区域映射到它们各自独立的地址空间中。就像多个线程访问共享数据一样,多个进程访问共享数据通常也需要同步。如果进程共享互斥量设置为PTHREAD_PROCESS_SHARED,就可以实现多个进程共享互斥量,如果设置为PTHREAD_PROCESS_PRIVATE,就是不允许多进程共享互斥量,只是单进程内使用互斥量。
可以使用pthread_mutexattr_getpshared函数查询pthread_mutexattr_t结构,得到它的进程共享属性,可以用pthread_mutexattr_setpshared函数修改进程共享属性。
#include    <pthread.h>
int    pthread_mutexattr_getpshared(const   pthread_mutexattr_t  *restrict  attr,  int  *restrict  pshared);
int    pthread_mutexattr_setpshared(pthread_mutexattr_t  *attr,  int  pshared);
返回值:若成功则返回0,否则返回错误编号。
2. 读写锁
        互斥量虽然可以起到锁的作用,但并行性不高,读写锁的并行性会更高些。互斥量要么是锁住状态要么是不加锁状态,而且一次只有一个线程可以对其加锁。读写锁可以有三种状态:读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一个线程可以占有写模式的读写锁,但是多个线程可以同时占有读模式的读写锁。
        当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞。当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是如果线程希望以写模式对此锁进行加锁,它必须阻塞直到所有的线程释放读锁。当读写锁处于读模式锁住状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁请求。这样可以避免读模式锁长期占有,而等待的写模式锁请求一直得不到满足。
         读写锁非常适合于对数据结构读的次数远大于写的情况。当读写锁在写模式下时,它所保护的数据结构就可以被安全地修改,因为当前只有一个线程可以在写模式下拥有这个锁。当读写锁在读模式下时,只要线程获得了读模式的读写锁,该锁所保护的数据结构可以被多个获得读模式锁的线程读取。
         读写锁也叫做共享 - 独占锁,当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。
         与互斥量一样,读写锁在使用之前必须初始化,在释放它们底层的内存前必须销毁。
#include  <pthread.h>
int     pthread_rwlock_init(pthread_rwlock_t  *restrict  rwlock, const  pthread_rwlockattr_t  *restrict  attr);
int     pthread_rwlock_destroy(pthread_rwlock  *rwlock);
返回值:若成功则返回0,否则返回错误编号。
说明:
1.读写锁通过调用pthread_rwlock_init进行初始化,如果希望读写锁有默认的属性,可以传一个空指针给attr。
2.在释放读写锁占用的内存之前,需要调用pthread_rwlock_destroy做清理工作。如果pthread_rwlock_init为读写锁分配了资源,pthread_rwlock_destroy将释放这些资源。如果在调用pthread_rwlock_destroy之前就释放了读写锁占用的内存空间,那么分配给这个锁的资源就丢失了。

要在读模式下锁定读写锁,需要调用pthread_rwlock_rdlock,要在写模式下锁定读写锁,需要调用pthread_rwlock_wrlock。不管以何种方式锁住读写锁,都可以调用pthread_rwlock_unlock进行解锁。
#include    <pthread.h>
int    pthread_rwlock_rdlock(pthread_rwlock_t   *rwlock);
int    pthread_rwlock_wrlock(pthread_rwlock_t   *rwlock);
int    pthread_rwlock_unlock(pthread_rwlock_t   *rwlock);
所有返回值都是:若成功则返回0,否则返回错误编号
说明:在实现读写锁的时候可能会对共享模式下可获取的锁的数量进行限制,所以需要检查pthread_rwlock_rdlock的返回值。即使pthread_rwlock_wrlock和pthread_rwlock_unlock有错误的返回值,如果锁设计合理的话,也不需要检查其返回值。错误返回值的定义只是针对不正确地使用读写锁的情况,例如未经初始化的锁,或者试图获取已拥有的锁从而可能产生死锁这样的错误返回等。
#include  <pthread.h>
int     pthread_rwlock_tryrdlock(pthread_rwlock_t   *rwlock);
int     pthread_rwlock_trywrlock(pthread_rwlock_t   *rwlock);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:可以获取锁时,函数返回0,否则,返回错误EBUSY。

读写锁属性
用pthread_rwlockattr_init初始化pthread_rwlockattr_t结构,用pthread_rwlockattr_destroy回收结构。
#include    <pthread.h>
int    pthread_rwlockattr_init(pthread_rwlockattr_t  *attr);
int    pthread_rwlockattr_destroy(pthread_rwlockattr_t   *attr);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:读写锁支持的唯一属性是进程共享属性,该属性与互斥量的进程共享属性相同。就像互斥量的进程属性一样,用一对函数来读取和设置读写锁的进程共享属性。
#include    <pthread.h>
int    pthread_rwlockattr_getpshared(const  pthread_rwlockattr_t   *restrict  attr, int  *restrict  pshared);
int    pthread_rwlockattr_setpshared(pthread_rwlockattr_t   *attr, int  pshared);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:虽然posix只定义了一个读写锁属性,但不同平台的实现可以自由地定义额外的  非标准的属性。
3. 条件变量
条件变量给多个线程提供了一个会合的场所。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。条件本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁住互斥量以后才能计算条件。
条件变量使用之前必须首先进行初始化,pthread_cond_t数据类型代表的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,可以使用pthread_cond_init函数进行初始化。
       在释放底层的内存空间之前,可以使用pthread_mutex_destroy函数对条件变量进行去除初始化。
#include    <pthread.h>
int    pthread_cond_init(pthread_cond_t    *restrict  cond, pthread_condattr_t   *restrict  attr);
int    pthread_cond_destroy(pthread_cond_t   *cond);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:如果要创建一个默认属性的条件变量,可以将pthread_cond_init函数的attr参数可以设置为NULL。

使用pthread_cond_wait等待条件变为真,如果在给定的时间内条件不能满足,那么会生成一个代表出错码的返回变量。
#include    <pthread.h>
int    pthread_cond_wait(pthread_cond_t   *restrict  cond,pthread_mutex_t  *restrict  mutex);
int    pthread_cond_timedwait(pthread_cond_t   *restrict  cond, pthread_mutex_t  *restrict  mutex, const struct  timespec *restrict  timeout);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:传递给pthread_cond_wait的互斥量对条件进行保护,调用者把锁住的互斥量传给函数。函数把调用线程放到等待条件的线程列表上,然后对互斥量解锁,这两个操作是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变
这两个操作是原子操作。这样就关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道,这样线程就不会错过条件的任何变化。pthread_cond_wait返回时,互斥量再次被锁住。
         pthread_cond_timedwait函数的工作方式与pthread_cond_wait函数相似,只是多了一个timeout。timeout值指定了等待的时间,它是通过timespec结构指定。时间值用秒数或者分秒数来表示,分秒数的单位是纳秒。
         struct   timespec  {
                         time_t     tv_sec;          /* seconds */
                         long         tv_nsec;       /* nanoseconds */
}
使用这个结构时,需要指定愿意等待多长时间,时间值是一个绝对数而不是相对数。例如,如果能等待3分钟,就需要把当前时间加上3分钟再转换到timespec结构,而不是把3分钟转换成timespec结构。可以使用gettimeofday获取用timeval结构表示的当前时间,然后把这个时间转换成timespec结构。要得到timeout值的绝对时间,可以使用下面的函数:
void
maketimeout(struct  timespec  *tsp,long  minutes)
{
            struct     timeval  now;
            gettimeofday(&now);
             tsp->tv_sec = now.tv_sec;
             tsp->tv_nsec = now.tv_usec * 1000;  /* usec  to  nsec  */
             tsp->tv_sec += minutes  *  60;
}
如果时间到了但条件还是没有出现,pthread_cond_timedwait将重新获取互斥量然后返回错误ETIMEDOUT。从pthread_cond_wait或者pthread_cond_timedwait调用成功返回时,线程需要重新计算条件,因为其他的线程可能已经在运行并改变了条件。

有两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数将唤醒等待该条件的某个线程,而pthread_cond_broadcast函数将唤醒等待该条件的所有线程。
#include     <pthread.h>
int     pthread_cond_signal(pthread_cond_t   *cond);
int     pthread_cond_broadcast(pthread_cond_t   *cond);
两者的返回值都是:若成功则返回0,否则返回错误编号
说明:调用pthread_cond_signal或者pthread_cond_broadcast,也称为向线程或者条件发送信号。必须注意一定要在改变条件状态以后再给线程发送信号。

条件变量属性
条件变量也有属性,与互斥量和读写锁类似,有一对函数用于初始化和回收条件变量属性。
#include   <pthread.h>
int    pthread_condattr_init(pthread_condattr_t  *attr);
int    pthread_condattr_destroy(pthread_condattr_t  *attr);
两者的返回值都是:若成功则返回0,否则返回错误编号
条件变量支持进程共享属性。
#include  <pthread.h>
int    pthread_condattr_getpshared(const  pthread_condattr_t  *restrict  attr, int  *restrict  pshared);
int    pthread_condattr_setpshared(const  pthread_condattr_t  *attr, int  pshared);
两者的返回值都是:若成功则返回0,否则返回错误编号






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值