互斥量与读写锁操作函数

    互斥变量是用 pthread_mutex_t 数据类型表示的。下面几个函数可操作互斥量。
#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);
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 <stdlib.h>
#include <pthread.h>

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 <pthread.h>
#include <time.h>
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict tsptr);
                            /* 返回值:若成功,返回 0;否则,返回错误编号 */

    该函数与 pthread_mutex_lock 基本等价,但它允许绑定线程阻塞时间。当达到超时时间时,它不会对互斥量进行加锁,而是返回错误码 ETIMEDOUT。这里的时间参数是一个绝对时间。
    下面这个示例通过对已有的互斥量进行再次加锁(这通常会造成死锁)来演示了如何用 pthread_mutex_timedlock 来避免永久阻塞。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pthread.h>

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);
	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);
	pr_time(tout);
	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

    说完互斥量,接下来再说说读写锁。
    读写锁也称共享互斥锁,它能在读取数据频率远高于写数据时提供比互斥量更高的并发性。当读写锁在写模式下时,它所保护的数据就可以被安全地修改,因为一次只有一个线程可以拥有写模式的锁。而当它在读模式下时,只要线程先获取了读模式下的读写锁,该锁所保护的数据就可以同时被多个获得读模式锁的线程读取。为避免读模式锁长期占用,一般如果有一个线程试图以写模式获取锁时,读写锁通常会阻塞随后的读模式锁请求。
    与互斥量相同,读写锁在使用前必须初始化,在释放其底层内存前必须销毁。
#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;否则,返回错误编号 */

    attr 参数是读写锁的属性,为 NULL 表示使用默认属性。XSI 扩展中也定义了 PTHREAD_RWLOCK_INITIALIZER 常量,用来对静态分配的读写锁进行初始化。
    可以调用 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 的返回值。
    Single UNIX Specification 还定义了读写锁原语的条件版本和带有超时的读写锁函数。
#include <pthread.h>
int 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 错误。

猜你喜欢

转载自aisxyz.iteye.com/blog/2397394
今日推荐