linux线程同步(3)-读写锁,linux线程


一.概述                                                   

读写锁与互斥量的功能类似,对临界区的共享资源进行保护!互斥量一次只让一个线程进入临界区,读写锁比它有更高的并行性。读写锁有以下特点:

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秒都不会有其他线程能进来!!!

相关内容