多线程对同一个变量进行操作时,总是有读有写。一般情况下,读的频率大于写的频率。多线程同时对一个变量进行读操作是被允许的,而同时对一个变量进行写操作或某些线程对变量进行读操作,另一些线程对变量进行写操作是不被允许的。如果使用互斥量,那同时只能有一个线程对变量进行操作,则影响的程序执行的效率。为此,pthread
库提供了读写锁。
1 读写锁属性类型
#define __SIZEOF_PTHREAD_RWLOCKATTR_T 8
typedef union
{
char __size[__SIZEOF_PTHREAD_RWLOCKATTR_T];
long int __align;
} pthread_rwlockattr_t;
2 初始化读写锁属性对象
int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
- 以默认值初始化一个读写锁属性对象
- 初始化一个已初始化的读写锁属性对象,将导致不确定性行为
- 使用一个已销毁的或未初始化的读写锁属性对象 ,将导致不确定性行为
- 在使用读写锁属性对象初始化一个或多个读写锁对象之后,修改或销毁该读写锁属性对象不影响已创建的读写锁对象
3 销毁读写锁属性对象
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
- 销毁一个读写锁属性对象
- 一个已销毁的读写锁属性对象可以被重新初始化
- 释放一个未初始化的读写锁属性对象,将导致不确定性行为
4 读写锁属性之进程共享属性
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *restrict attr,
int *restrict pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared);
获取或设置读写锁属性对象中的进程共享
属性。进程共享
属性可以设置为以下值:
-
PTHREAD_PROCESS_SHARED
设置为该值,允许任何有权限访问的线程访问分配读写锁的内存,即使读写锁是在由多个进程共享的内存中分配的
-
PTHREAD_PROCESS_PRIVATE
读写锁只能由与初始化读写锁的线程在统一进程内创建的线程操作。如果不同进程的线程尝试操作该读写锁,则行为是未定义。该值为默认值。
注意
设置或获取一个未初始化的读写锁属性对象,将导致不确定性行为。
5 读写锁属性之锁类型
int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref);
int pthread_rwlockattr_getkind_np(const pthread_rwlockattr_t *attr, int *pref);
获取或设置读写锁属性对象中的锁类型
属性。锁类型
属性有以下可选值:
-
PTHREAD_RWLOCK_PREFER_READER_NP
该属性是默认值。一个线程可以拥有多个读锁。根据
The Single Unix Specification
,当读取器尝试放置锁,但无写入锁的写入器正在等待时,此时的行为是未定义的。当锁类型被设置为PTHREAD_RWLOCK_PREFER_READER_NP
时,则读写器优先。这意味着机器写入器正在等待,读取器将收到请求的锁。只要读取器一直存在,写入器将处于饥饿状态。 -
PTHREAD_RWLOCK_PREFER_WRITER_NP
This is intended as the write lock analog of
PTHREAD_RWLOCK_PREFER_READER_NP
. This is ignored byglibc
because thePOSIX
requirement to support recursive writer locks would cause this option to create trivial deadlocks; instead usePTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
which ensures the application developer will not take recursive read locks thus avoiding deadlocks. -
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
Setting the lock kind to this avoids writer starvation as long as any read locking is not done in a recursive fashion.
6 初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
const pthread_rwlockattr_t *restrict attr);
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
7 销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
8 读写锁之加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
9 读写锁之加写锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
10 销毁读写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
11 案例:读写锁的使用
-
源码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int g_num = 0; pthread_rwlock_t rwlock; void *start_routine_01(void *ptr) { for (size_t i = 0; i < 5; i++) { pthread_rwlock_rdlock(&rwlock); for (size_t i = 0; i < 2; i++) { printf("读线程(%lu)获取全局变量当前值(%d)\n", pthread_self(), g_num); sleep(1); } pthread_rwlock_unlock(&rwlock); sleep(1); } return (void *)NULL; } void *start_routine_02(void *ptr) { for (size_t i = 0; i < 5; i++) { pthread_rwlock_rdlock(&rwlock); for (size_t i = 0; i < 2; i++) { printf("读线程(%lu)获取全局变量当前值(%d)\n", pthread_self(), g_num); sleep(1); } pthread_rwlock_unlock(&rwlock); sleep(1); } return (void *)NULL; } void *start_routine_03(void *ptr) { for (size_t i = 0; i < 5; i++) { pthread_rwlock_wrlock(&rwlock); printf("写线程(%lu)设置全局变量当前值(%d)\n", pthread_self(), ++g_num); pthread_rwlock_unlock(&rwlock); sleep(2); } return (void *)NULL; } int main(int argc, char const *argv[]) { pthread_rwlockattr_t attr; pthread_rwlockattr_init(&attr); pthread_rwlock_init(&rwlock, &attr); pthread_rwlockattr_destroy(&attr); pthread_t thread_id_01, thread_id_02, thread_id_03; pthread_create(&thread_id_01, NULL, start_routine_01, NULL); pthread_create(&thread_id_02, NULL, start_routine_02, NULL); pthread_create(&thread_id_03, NULL, start_routine_03, NULL); pthread_join(thread_id_01, NULL); pthread_join(thread_id_02, NULL); pthread_join(thread_id_03, NULL); pthread_rwlock_destroy(&rwlock); exit(EXIT_SUCCESS); }
-
输出
读线程(140456693409536)获取全局变量当前值(0)
读线程(140456685016832)获取全局变量当前值(0)
读线程(140456693409536)获取全局变量当前值(0)
读线程(140456685016832)获取全局变量当前值(0)
写线程(140456676624128)设置全局变量当前值(1)
读线程(140456685016832)获取全局变量当前值(1)
读线程(140456693409536)获取全局变量当前值(1)
读线程(140456685016832)获取全局变量当前值(1)
读线程(140456693409536)获取全局变量当前值(1)
写线程(140456676624128)设置全局变量当前值(2)
读线程(140456685016832)获取全局变量当前值(2)
读线程(140456693409536)获取全局变量当前值(2)
读线程(140456685016832)获取全局变量当前值(2)
读线程(140456693409536)获取全局变量当前值(2)
写线程(140456676624128)设置全局变量当前值(3)
读线程(140456685016832)获取全局变量当前值(3)
读线程(140456693409536)获取全局变量当前值(3)
读线程(140456685016832)获取全局变量当前值(3)
读线程(140456693409536)获取全局变量当前值(3)
写线程(140456676624128)设置全局变量当前值(4)
读线程(140456685016832)获取全局变量当前值(4)
读线程(140456693409536)获取全局变量当前值(4)
读线程(140456685016832)获取全局变量当前值(4)
读线程(140456693409536)获取全局变量当前值(4)
写线程(140456676624128)设置全局变量当前值(5)