POSIX 读写锁

更新时间:
2024-05-29
下载文档

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
文档内容是否对您有所帮助?
有帮助
没帮助