一.概述
读写锁与互斥量的功能类似,对临界区的共享资源进行保护!互斥量一次只让一个线程进入临界区,读写锁比它有更高的并行性。读写锁有以下特点:
1.如果一个线程用读锁锁定了临界区,那么其他线程也可以用读锁来进入临界区,这样就可以多个线程并行操作。但这个时候,如果再进行写锁加锁就会发生阻塞,写锁请求阻塞后,后面如果继续有读锁来请求,这些后来的读锁都会被阻塞!这样避免了读锁长期占用资源,防止写锁饥饿!
2.如果一个线程用写锁锁住了临界区,那么其他线程不管是读锁还是写锁都会发生阻塞!
二.函数接口
1.创建读写锁
1.1:宏常量初始化
1 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
1.2:函数初始化
1 #include <pthread.h> 2 3 int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
rwlock:读写锁的pthread_rwlock_t结构指针
attr:读写锁的属性结构指针。不需要别的属性默认为NULL。
2.读写锁加锁与解锁
1 #include <pthread.h> 2 3 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); 4 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 5 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
rwlock:创建的读写锁指针
3.其他类型的加锁
1 #include <pthread.h> 2 #include <time.h> 3 4 5 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); 6 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 7 8 int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout); 9 int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abs_timeout);
try类函数加锁:如果获取不到锁,会立即返回错误EBUSY!
timed类函数加锁:如果规定的时间内获取不到锁,会返回ETIMEDOUT错误!
4.销毁读写锁
1 #include <pthread.h> 2 3 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
三.简单的例子
创建4个线程,2个线程读锁,2个线程写锁,观察4个线程进入临界区的顺序:
1 /** 2 * @file pthread_rwlock.c 3 */ 4 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <unistd.h> 9 #include <pthread.h> 10 11 /* 初始化读写锁 */ 12 pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 13 /* 全局资源 */ 14 int global_num = 10; 15 16 void err_exit(const char *err_msg) 17 { 18 printf("error:%s\n", err_msg); 19 exit(1); 20 } 21 22 /* 读锁线程函数 */ 23 void *thread_read_lock(void *arg) 24 { 25 char *pthr_name = (char *)arg; 26 27 while (1) 28 { 29 /* 读加锁 */ 30 pthread_rwlock_rdlock(&rwlock); 31 32 printf("线程%s进入临界区,global_num = %d\n", pthr_name, global_num); 33 sleep(1); 34 printf("线程%s离开临界区...\n", pthr_name); 35 36 /* 读解锁 */ 37 pthread_rwlock_unlock(&rwlock); 38 39 sleep(1); 40 } 41 42 return NULL; 43 } 44 45 /* 写锁线程函数 */ 46 void *thread_write_lock(void *arg) 47 { 48 char *pthr_name = (char *)arg; 49 50 while (1) 51 { 52 /* 写加锁 */ 53 pthread_rwlock_wrlock(&rwlock); 54 55 /* 写操作 */ 56 global_num++; 57 printf("线程%s进入临界区,global_num = %d\n", pthr_name, global_num); 58 sleep(1); 59 printf("线程%s离开临界区...\n", pthr_name); 60 61 /* 写解锁 */ 62 pthread_rwlock_unlock(&rwlock); 63 64 sleep(2); 65 } 66 67 return NULL; 68 } 69 70 int main(void) 71 { 72 pthread_t tid_read_1, tid_read_2, tid_write_1, tid_write_2; 73 74 /* 创建4个线程,2个读,2个写 */ 75 if (pthread_create(&tid_read_1, NULL, thread_read_lock, "read_1") != 0) 76 err_exit("create tid_read_1"); 77 78 if (pthread_create(&tid_read_2, NULL, thread_read_lock, "read_2") != 0) 79 err_exit("create tid_read_2"); 80 81 if (pthread_create(&tid_write_1, NULL, thread_write_lock, "write_1") != 0) 82 err_exit("create tid_write_1"); 83 84 if (pthread_create(&tid_write_2, NULL, thread_write_lock, "write_2") != 0) 85 err_exit("create tid_write_2"); 86 87 /* 随便等待一个线程,防止main结束 */ 88 if (pthread_join(tid_read_1, NULL) != 0) 89 err_exit("pthread_join()"); 90 91 return 0; 92 }
2个线程函数的临界区里面都sleep(1),测试给足够的时间看其他线程能不能进来。64行,写锁函数里面,sleep(2),因为写锁请求会阻塞后面的读锁,2个写锁一起请求会让读锁饥饿,所以比39行的sleep(1)多一秒!
编译运行:
可以看到,读锁可以一起进入临界区,而写锁在临界区里面等1秒都不会有其他线程能进来!!!