POSIX 读写锁
在前面我们举了一个共享资源竞争的例子:两个线程需要同时将同一个变量 V(初始值为 0)进行自增操作。解决共享资源竞争的办法是加入一把锁,在访问变量 V 前占有该锁,在访问后释放该锁。一般情况下,我们可以使用初始值为 TRUE 的二进制信号量或初始值为 1 的计数信号量或互斥信号量作为锁。
现在我们把这个例子稍加改动,有十个线程(线程 A、线程 1...线程 9)和一个变量 V,线程 A 需要写变量 V,线程[1-9]需要读变量 V。显然变量 V 的读者线程多于写者线程。在这种情况下,如果我们继续使用初始值为 TRUE 的二进制信号量或初始值为 1 的计数信号量或互斥信号量作为锁,那么当有一个读者线程占有该锁时,则其他的读者线程都会阻塞在该锁上,这显然降低了读取共享资源时的并发效率,但即使多个线程同时直接对变量 V 进行读操作,也不会使变量 V 的值混乱。
为了解决普通锁机制在对共享资源“读多写少”情况下读并发效率低下的问题,POSIX 标准定义了读写锁及其操作,读写锁具有三种状态:读状态、写状态、解锁状态。读写锁规定:处于读状态的读写锁可以再次锁定任意读锁,而请求的写锁将在读锁解锁之后首先被响应(SylixOS 支持写锁优先原则);处于写状态的读写锁不会响应任何锁,即任何加锁请求都将失败。
POSIX 读写锁的类型为 pthread_rwlock_t。使用时定义一个 pthread_rwlock_t 类型的变量,如:
pthread_rwlock_t rwlock;
一个 POSIX 读写锁必须要调用 pthread_rwlock_init 函数初始化之后才能使用。
线程如果需要等待一个读写锁,根据其对共享资源的使用(读取还是写入),分别调用 pthread_rwlock_rdlock 或 pthread_rwlock_wrlock 函数,中断服务程序不能调用任何 POSIX 读写锁函数。调用 pthread_rwlock_unlock 函数可以解锁一个读写锁。
当一个读写锁使用完毕后(并确保以后也不再使用),应该调用 pthread_rwlock_destroy 函数删除它,SylixOS 会回收该读写锁占用的内核资源。
创建一个 POSIX 读写锁需要使用一个 POSIX 读写锁属性块作为参数。POSIX 读写锁属性块的类型为 pthread_rwlockattr_t。使用时定义一个 pthread_rwlockattr_t 类型的变量,如:
pthread_rwlockattr_t rwlockattr;
读写锁属性块
读写锁属性块的初始化和删除
#include <pthread.h>
int pthread_rwlockattr_init(pthread_rwlockattr_t *prwlockattr);
函数 pthread_rwlockattr_init 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 prwlockattr 是 POSIX 读写锁属性块的指针。
#include <pthread.h>
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *prwlockattr);
函数 pthread_rwlockattr_destroy 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 prwlockattr 是 POSIX 读写锁属性块的指针。
设置和获取读写锁属性块的进程共享属性
#include <pthread.h>
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *prwlockattr,
int pshared);
函数 pthread_rwlockattr_setpshared 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 prwlockattr 是 POSIX 读写锁属性块的指针。
- 参数 pshared 标识了 POSIX 读写锁属性块是否进程共享。
#include <pthread.h>
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *prwlockattr,
int *pshared);
函数 pthread_rwlockattr_getpshared 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 prwlockattr 是 POSIX 读写锁属性块的指针。
- 输出参数 pshared 标识了 POSIX 读写锁属性块是否进程共享。
读写锁
读写锁的初始化和删除
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *prwlock,
const pthread_rwlockattr_t *prwlockattr);
函数 pthread_rwlock_init 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 prwlock 是 POSIX 读写锁的指针。
- 参数 prwlockattr 是 POSIX 读写锁属性对象的指针,可以为 NULL。
#include <pthread.h>
int pthread_rwlock_destroy(pthread_rwlock_t *prwlock);
函数 pthread_rwlock_destroy 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 prwlock 是 POSIX 读写锁的指针。
读写锁的读等待
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *prwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *prwlock);
int pthread_rwlock_timedrdlock(pthread_rwlock_t *prwlock,
const struct timespec *abs_timeout);
以上三个函数原型分析:
- 函数成功返回 0,失败返回错误号。
- 参数 prwlock 是 POSIX 读写锁的指针。
- 参数 abs_timeout 是等待的绝对超时时间。
pthread_rwlock_timedrdlock 是 pthread_rwlock_rdlock 的带等待超时时间的版本,abs_timeout 为等待的绝对超时时间(见“时间管理”章节)。
pthread_rwlock_tryrdlock 是 pthread_rwlock_rdlock 的“尝试等待”版本,在读写锁已经被写锁占有时,pthread_rwlock_rdlock 将阻塞直至被唤醒,而 pthread_rwlock_tryrdlock 将立即返回,并返回错误号 EBUSY。
读写锁的写等待
#include <pthread.h>
int pthread_rwlock_wrlock(pthread_rwlock_t *prwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *prwlock);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *prwlock,
const struct timespec *abs_timeout);
以上三个函数原型分析:
- 函数成功返回 0,失败返回错误号。
- 参数 prwlock 是 POSIX 读写锁的指针。
- 参数 abs_timeout 是等待的绝对超时时间。
pthread_rwlock_timedwrlock 是 pthread_rwlock_wrlock 的带等待超时时间的版本,abs_timeout 为等待的绝对超时时间。
pthread_rwlock_trywrlock 是 pthread_rwlock_wrlock 的“尝试等待”版本,在读写锁已经被读锁占有时,pthread_rwlock_wrlock 将阻塞直至被唤醒,而 pthread_rwlock_trywrlock 将立即返回,并返回错误号 EBUSY。
读写锁的解锁
#include <pthread.h>
int pthread_rwlock_unlock(pthread_rwlock_t *prwlock);
函数 pthread_rwlock_unlock 原型分析:
- 此函数成功返回 0,失败返回错误号。
- 参数 prwlock 是 POSIX 读写锁的指针。
下面程序展示了 POSIX 读写锁的使用,程序创建四个读者线程和一个写者线程及一个 POSIX 读写锁,写者线程对变量 count 进行自增操作,四个读者线程变量 count 进行打印,使用 POSIX 读写锁作为访问变量 count 的互斥手段。
#include <stdio.h>
#include <pthread.h>
#define READ_THREAD_NR 4
static pthread_rwlock_t lock;
static int count = 0;
static void *thread_read (void *arg)
{
while (1) {
pthread_rwlock_rdlock(&lock);
printf("thread_read(): count = %d\n", count);
pthread_rwlock_unlock(&lock);
sleep(1);
}
return (NULL);
}
static void *thread_write (void *arg)
{
while (1) {
pthread_rwlock_wrlock(&lock);
count++;
pthread_rwlock_unlock(&lock);
sleep(1);
}
return (NULL);
}
int main (int argc, char *argv[])
{
pthread_t threadrd_tid[READ_THREAD_NR];
pthread_t threadwr_tid;
int ret;
int i;
ret = pthread_rwlock_init(&lock, NULL);
if (ret != 0) {
fprintf(stderr, "rwlock create failed.\n");
return (-1);
}
for (i = 0; i < READ_THREAD_NR; i++) {
ret = pthread_create(&threadrd_tid[i], NULL, thread_read, NULL);
if (ret != 0) {
fprintf(stderr, "pthread create failed.\n");
return (-1);
}
}
ret = pthread_create(&threadwr_tid, NULL, thread_write, NULL);
if (ret != 0) {
fprintf(stderr, "pthread create failed.\n");
return (-1);
}
for (i = 0; i < READ_THREAD_NR; i++) {
pthread_join(threadrd_tid[i], NULL);
}
pthread_join(threadwr_tid, NULL);
pthread_rwlock_destroy(&lock);
return (0);
}
在 SylixOS Shell 下运行程序:
# ./POSIX_Read-Write_Lock
thread_read(): count = 0
thread_read(): count = 0
thread_read(): count = 0
thread_read(): count = 0
thread_read(): count = 1
thread_read(): count = 1
thread_read(): count = 1
thread_read(): count = 1
......
# ts
NAME TID PID PRI STAT LOCK SAFE DELAY PAGEFAILS FPU CPU
--------------------- ------- ----- --- ---- ---- ---- ---------- --------- --- ---
POSIX_Read-Write_Lock 4010072 36 200 JOIN 0 0 1 USE 0
pthread 4010073 36 200 SLP 0 188 0 0
pthread 4010074 36 200 SLP 0 188 0 0
pthread 4010075 36 200 SLP 0 188 0 0
pthread 4010076 36 200 SLP 0 188 0 0
pthread 4010077 36 200 SLP 0 188 0 0