互斥量既可以像静态变量那样分配,也可以在运行时动态创建(例如,通过malloc()在一块内存中分配)。这里先介绍静态分配,随后介绍动态分配。
互斥量是属于pthrad_mutex_t类型的变量。在使用之前必须对其初始化。对于静态分配的互斥量而言,可如下所示,将PTHREAD_MUTEX_INITIALIZER赋给变量:
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
初始化之后,互斥量处于未锁定状态。函数pthread_mutex_lock()可以锁定某一互斥量,而函数pthread_mutex_unlock()则可以将一个互斥量解锁。
#include
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
Both return 0 on success, or a positive error number on error.
pthread_mutex_lock()将锁定互斥量并立即返回。pthread_lock()会一直阻塞,直至该互斥量被解锁,调用将锁定该互斥量并返回。pthread_mutex_lock()调用的线程自身之前已然将目标互斥量锁定,对于互斥量的默认类型而言,可能会产生两种后果——视具体实现而定:线程陷入死锁(deadlock),因为试图锁定已为自己所持有的互斥量而遭到阻塞;或者调用失败,返回EDEADLK 错误。在Linux上,默认情况下线程会发生死锁。pthread_mutex_unlock()将解锁之前已遭调用线程锁定的互斥量,以下行为均属错误:
如果有不止一个线程在等待获取由函数pthread_mutex_unlock()解锁的互斥量,则无法判断究竟哪个线程如愿以偿。
Pthread API提供了pthread_mutex_lock()函数的两个变体:ptrhead_mutex_trylock()和ptrhead_mutex_timelock()。
pthread_mutex_trylock()会失败并返回EBUSY错误,除此之外,该函数与pthread_mutex_lock()行为相同。abstime(设置线程等待获取互斥量时休眠的时间限制)外,pthread_mutex_timedlock()与pthread_mutex_lock()没有差别。如果参数abstime指定的时间间隔期满,而调用线程又没有获得对互斥量的拥有权,那么函数pthread_mutex_timedlock()返回ETIMEDOUT错误。函数pthread_mutex_trylock()和pthread_mutex_timedlock()比pthread_mutex_lock()的使用频率要低很多。在大多数经过良好设计的应用程序中,线程对互斥量的持有时间应尽可能短,以避免妨碍其他线程的并发执行。这也保证了遭阻塞的其他线程可以很快获取对互斥量的锁定,若某一线程使用pthread_mutex_trylock()周期性地轮询是否可以对互斥量加锁,则有可能要承担这样的风险:当队列中的其他线程通过调用pthread_mutex_lock()相继获得对互斥量的访问时,该线程将始终与此互斥量无缘。
静态初始值PTHREAD_MUTEX_INITIALIZER,只能用于对如下互斥量进行初始化:经由静态分配且携带默认属性。
其他情况下,必须调用pthread_mutex_init()对互斥量进行动态初始化。
#include
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);
Returns 0 on success, or a positive error number on error.
pthread_mutexattr_t类型对象的指针,该对象在函数调用之前已经过了初始化处理,用于定义互斥量的属性。SUSv3规定,初始化一个业已初始化的互斥量将导致未定义的行为,应当避免这一行为。
以下情况必须使用pthread_mutex_init()而非静态初始化互斥量。
pthread_mutex_t类型的字段来存放互斥量,借以保护对该结构的访问。(也即是,只有静态分配且使用默认属性的互斥量才用静态初始化。)
当不再需要经由自动或动态分配的互斥量时,应使用pthread_mutex_destroy()将其销毁。(对于使用PHREAD_MUTEX_INTIALIZER 静态初始化的互斥量,无需调用pthread_mutex_destroy()。)
#include
int pthread_mutex_destroy(pthread_mutex_t * mutex);
Returns 0 on success, or a positive error number on error.
只有互斥量处于未锁定状态,且后续也无任何线程企图锁定它时,将其销毁才是安全的。
若互斥量驻留于动态分配的一片内存区域中,应在释放(free)此内存区域前将其销毁。对于自动分配的互斥量,也应在宿主函数返回前将其销毁。
经由pthread_mutex_destroy()销毁的互斥量,可调用pthread_mutex_init()对其重新初始化。
前面的规则归纳如下:
准确地说,上述情况的结果取决于互斥量类型(type),SUSv3定义了以下互斥量类型:
Linux的线程实现针对以上各种类型的互斥量提供了非标准的静态初始值(例如,PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP),以便对那些通过静态分配的互斥量进行初始化,而无需使用pthread_mutex_init()函数。不过,为保证程序可移植性,应该避免使用这些初始值。
除了上述类型,SUSv3还定义了PTHREAD_MUTEX_DEFAULT类型:
下例创建一个带有错误检查属性的互斥量:
pthread_mutex_t mtx;
pthread_mutexattr_t mtxAttr;
pthread_mutexattr_init(&mtxAttr);
pthread_mutexattr_settype(&mtxAttr, PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(mtx, &mtxAttr);
pthread_mutexattr_destroy(&mtxAttr);