线程同步之读写锁
一、读写锁的原理
读写锁与互斥量类似,不过读写锁有更高的并行性。互斥量要么加锁要么不加锁,而且同一时刻只允许一个线程对其加锁。对于一个变量的读取,完全可以让多个线程同时进行操作 。
二、读写锁的状态
一把读写锁具备三种状态:
1. 读模式下加锁状态 (读锁)
2. 写模式下加锁状态 (写锁)
3. 不加锁状态
读写锁的特点如下:
如果有其它线程读数据,则允许其它线程执行读操作,但不允许写操作。
如果有其它线程写数据,则其它线程都不允许读、写操作。
- 如果某线程申请了读锁,其它线程可以再申请读锁,但不能申请写锁。
- 如果某线程申请了写锁,其它线程不能申请读锁,也不能申请写锁。
举例子来说:
- 线程A加写锁成功,线程B请求读锁
- 线程B堵塞
- 线程A持有读锁,线程B请求写锁
- 线程B堵塞
- 线程A拥有读锁,线程B请求读锁
- 线程B加锁成功
- 线程A持有读锁,然后线程B请求写锁,然后线程C请求读锁
- B阻塞,C阻塞–写的优先级高
- A解锁,B线程加锁成功,C堵塞
- B解锁,C加读锁成功
- 线程A持有写锁,然后线程B请求读锁,然后线程C请求写锁
- BC堵塞
- A解锁,C加写锁成功,B继续阻塞
- C解锁,B加读锁成功
读写锁也叫共享-独占锁。当读写锁以读模式锁住时,它是以共享模式锁住的;当它以写模式锁住时,它是以独占模式锁住的。写独占、读共享。
三、函数
1)初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
功能:
用来初始化 rwlock 所指向的读写锁。
参数:
rwlock:指向要初始化的读写锁指针。
attr:读写锁的属性指针。如果 attr 为 NULL 则会使用默认的属性初始化读写锁,否则使用指定的 attr 初始化读写锁。
可以使用宏 PTHREAD_RWLOCK_INITIALIZER 静态初始化读写锁,比如:
pthread_rwlock_t my_rwlock = PTHREAD_RWLOCK_INITIALIZER;
这种方法等价于使用 NULL 指定的 attr 参数调用 pthread_rwlock_init() 来完成动态初始化,不同之处在于PTHREAD_RWLOCK_INITIALIZER 宏不进行错误检查。
返回值:
成功:0,读写锁的状态将成为已初始化和已解锁。
失败:非 0 错误码。
2)申请读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock );
功能:
以阻塞方式在读写锁上获取读锁(读锁定)。如果没有写者持有该锁,并且没有写者阻塞在该锁上,则调用线程会获取读锁。如果调用线程未获取读锁,则它将阻塞直到它获取了该锁。一个线程可以在一个读写锁上多次执行读锁定。线程可以成功调用 pthread_rwlock_rdlock() 函数 n 次,但是之后该线程必须调用 pthread_rwlock_unlock() 函数 n 次才能解除锁定。
参数:
rwlock:读写锁指针。
返回值:
成功:0
失败:非 0 错误码
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
用于尝试以非阻塞的方式来在读写锁上获取读锁。如果有任何的写者持有该锁或有写者阻塞在该读写锁上,则立即失败返回。
3)申请写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock );
功能:
在读写锁上获取写锁(写锁定)。如果没有写者持有该锁,并且没有写者读者持有该锁,则调用线程会获取写锁。如果调用线程未获取写锁,则它将阻塞直到它获取了该锁。
参数:
rwlock:读写锁指针。
返回值:
成功:0
失败:非 0 错误码
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
用于尝试以非阻塞的方式来在读写锁上获取写锁。如果有任何的读者或写者持有该锁,则立即失败返回。
4)解锁
int pthread_rwlock_unlock (pthread_rwlock_t *rwlock);
功能:
无论是读锁或写锁,都可以通过此函数解锁。
参数:
rwlock:读写锁指针。
返回值:
成功:0
失败:非 0 错误码
5)销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
功能:
用于销毁一个读写锁,并释放所有相关联的资源(所谓的所有指的是由 pthread_rwlock_init() 自动申请的资源) 。
参数:
rwlock:读写锁指针。
返回值:
成功:0
失败:非 0 错误码
代码实例:
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4
5 //读写锁
6 pthread_rwlock_t rwlock;
7
8 //全局变量
9 int No = 1;
10
11 //写操作
12 void *fun1(void* arg)
13 {
14 while(1)
15 {
16 pthread_rwlock_wrlock(&rwlock);
17 No++;
18 printf("write thread \n");
19 pthread_rwlock_unlock(&rwlock);
20 sleep(2);
21 }
22 }
23
24
25 //读操作
26 void *fun2(void* arg)
27 {
28 while(1)
29 {
30 pthread_rwlock_rdlock(&rwlock);
31 printf("first read thread :%d\n",No);
32 pthread_rwlock_unlock(&rwlock);
33 sleep(2);
34 }
35 }
36
37 //读操作2
38 void *fun3(void *arg)
39 {
40 while(1)
41 {
42 pthread_rwlock_rdlock(&rwlock);
43 printf("second read thread:%d\n",No);
44 pthread_rwlock_unlock(&rwlock);
45 sleep(2);
46 }
47 }
48
49 int main()
50 {
51 pthread_t tid1,tid2,tid3;
52 //初始化读写锁
53 pthread_rwlock_init(&rwlock, NULL);
54
55 //创建线程
56 pthread_create(&tid1,NULL,fun1,NULL);
57 pthread_create(&tid2,NULL,fun2,NULL);
58 pthread_create(&tid3,NULL,fun3,NULL);
59
60 //回收资源
61 pthread_join(tid1,NULL);
62 pthread_join(tid2,NULL);
63 pthread_join(tid3,NULL);
64
65 //销毁读写锁
66 pthread_rwlock_destroy(&rwlock);
67
68 return 0;
69 }
结果:
wz@wz-machine:~/linux$ ./rw_lock
second read thread:1
first read thread :1
write thread
first read thread :2
second read thread:2
write thread
second read thread:3
first read thread :3
write thread
first read thread :4
second read thread:4
我们试试没有加锁的现象:
把读写锁注释掉的结果是:
first read thread :3
second read thread:3
write thread
second read thread:4
first read thread :4
write thread
first read thread :5
second read thread:5
write thread
second read thread:6
write thread
first read thread :7
write thread
first read thread :8
second read thread:8
first read thread :8
second read thread:8
write thread
很明显我们可以看出来问题;数据混乱。