#includeint pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); int pthread_mutex_destroy(pthread_mutex_t *mutex); 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;否则,返回错误编号 */
使用互斥量前必须先对它进行初始化,可把它设置为常量 PTHREAD_MUTEX_INITIALIZER(只适用于静态分配的互斥量),也可以通过 pthread_mutex_init 函数进行初始化。要用默认的属性初始化互斥量,只需把 pthread_mutex_init 的参数 attr 设为 NULL。如果互斥量是动态分配的,则释放内存前需要调用 pthread_mutex_destroy。
函数 pthread_mutex_lock 和 pthread_mutex_unlock 可分别对互斥量进行加锁和解锁。如果互斥量已经上锁,调用线程将阻塞直到互斥量被解锁。如果线程不希望被阻塞,可以使用 pthread_mutex_trylock 尝试对互斥量加锁。若调用 pthread_mutex_trylock 时互斥量未上锁,则该函数就锁住互斥量,然后直接返回 0,否则该函数就会失败,然后返回 EBUSY。
下面这段代码描述了如何用互斥量来保护某个数据结构。当有多个线程要访问动态分配的对象时,可以在对象中嵌入引用计数,确保在所有使用该对象的线程完成数据访问之前,该对象的内存空间不会被释放。
#include#include struct foo{ int f_id; int f_count; pthread_mutex_t f_lock; /*...more stuff here...*/ }; struct foo* foo_alloc(int id){ // allocate the object struct foo *foop; if((foop=molloc(sizeof(struct foo))) != NULL){ foop->f_id = id; foop->f_count = 1; if(pthread_mutex_init(&foop->f_lock, NULL) != 0){ free(foop); return NULL } /*...continue initialization...*/ } return foop; } void foo_hold(struct foo *foop){ // add a reference to the object pthread_mutex_lock(&foop->f_lock); foop->f_count++; pthread_mutex_unlock(&foop->f_lock); } void foo_rele(struct foo *foop){ // release a reference to the object pthread_mutex_lock(&foop->f_lock); if(--foop->f_count == 0){ pthread_mutex_unlock(&foop->f_lock); pthread_mutex_destroy(&foop->f_lock); free(foop); }else{ pthread_mutex_unlock(&foop->f_lock); } }
为避免线程在获取锁时永久阻塞,可以使用函数 pthread_mutex_timedlock。
#include#include int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr); /* 返回值:若成功,返回 0;否则,返回错误编号 */
该函数与 pthread_mutex_lock 基本等价,但它允许绑定线程阻塞时间。当达到超时时间时,它不会对互斥量进行加锁,而是返回错误码 ETIMEDOUT。这里的时间参数是一个绝对时间。
下面这个示例通过对已有的互斥量进行再次加锁(这通常会造成死锁)来演示了如何用 pthread_mutex_timedlock 来避免永久阻塞。
#include#include #include #include #include void pr_time(struct timespec tout){ char buf[64]; struct tm *tmp = localtime(&tout.tv_sec); strftime(buf, sizeof(buf), "%r", tmp); printf("current time is: %s\n", buf); } int main(void){ pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&lock); printf("mutex is locked\n"); struct timespec tout; clock_gettime(CLOCK_REALTIME, &tout); pr_time(tout); tout.tv_sec += 5; // 5 seconds from now // caution: this could lead to deadlock int err = pthread_mutex_timedlock(&lock, &tout); clock_gettime(CLOCK_REALTIME, &tout); pr_time(tout); pthread_mutex_unlock(&lock); if(err != 0) printf("can't lock mutex again: %s\n", strerror(err)); else printf("mutex locked again!\n"); exit(0); }
编译后运行:
$ gcc timeoutLock.c -lpthread -lrt -o timeoutLock.out $ ./timeoutLock.out mutex is locked current time is: 10:37:07 AM current time is: 10:37:12 AM can't lock mutex again: Connection timed out
说完互斥量,接下来再说说读写锁。
读写锁也称共享互斥锁,它能在读取数据频率远高于写数据时提供比互斥量更高的并发性。当读写锁在写模式下时,它所保护的数据就可以被安全地修改,因为一次只有一个线程可以拥有写模式的锁。而当它在读模式下时,只要线程先获取了读模式下的读写锁,该锁所保护的数据就可以同时被多个获得读模式锁的线程读取。为避免读模式锁长期占用,一般如果有一个线程试图以写模式获取锁时,读写锁通常会阻塞随后的读模式锁请求。
与互斥量相同,读写锁在使用前必须初始化,在释放其底层内存前必须销毁。
#includeint pthread_rwlock_init( pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); int pthread_rwlock_destroy(pthread_rwlock *rwlock); /* 两个函数返回值:若成功,返回 0;否则,返回错误编号 */
attr 参数是读写锁的属性,为 NULL 表示使用默认属性。XSI 扩展中也定义了 PTHREAD_RWLOCK_INITIALIZER 常量,用来对静态分配的读写锁进行初始化。
可以调用 pthread_rwlock_rdlock 和 pthread_rwlock_wrlock 分别在读模式和写模式下锁定读写锁,这两种方式都可以调用 pthread_rwlock_unlock 来进行解锁。
#includeint 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 的返回值。
Single UNIX Specification 还定义了读写锁原语的条件版本和带有超时的读写锁函数。
#includeint pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_timedrdlock( pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr); int pthread_rwlock_timedwrlock( pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr); /* 所有函数返回值:若成功,返回 0;否则,返回错误编号 */
可以获取锁时,前两个函数返回 0,否则,它们返回错误 EBUSY。后两个函数如果在到达指定的时刻时还不能获取锁就将返回 ETIMEDOUT 错误。