【IPC】读写锁

读写锁是同步的又一种形式,与互斥锁不同,互斥锁同时只能被一个线程获取,而读写锁可以同时被多个线程获取读锁,这在一定程度上提高了程序的并发性,写锁也同样是只能被一个线程获取。

读写锁的数据类型为pthread_rwlock_t,静态分配时可初始化为PTHREAD_RWLOCK_INITIALIZER,动态分配时调用函数pthread_rwlock_init完成。下面是获取与释放读写锁的几个函数。

#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

pthread_rwlock_rdlock获取一个读锁,如果将要获取的读锁被某个写入者持有,将会阻塞,而pthread_rwlock_tryrdlock立即返回一个EBUSY错误却不会阻塞。pthread_rwlock_wrlock获取一个写锁,如果将要获取的写锁被某个写入者或读出者持有都将发生阻塞,而pthread_rwlock_trywrlock也是立即返回EBUSY并不阻塞。pthread_rwlock_unlock用于释放某个读写锁,pthread_rwlock_destroy则用来销毁一个读写锁。

读写锁使用pthread_rwlock_init初始化时,如果attr参数为空,将使用属性缺省值,不过也可以使用下面几个读写锁属性相关的函数进行动态操作。

int pthread_rwlockattr_init(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr);
int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *attrint *pshared);
int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attrint *pshared);

读写锁属性的数据类型为pthread_rwlockattr_t,pthread_rwlockattr_setpshared函数的pshared参数可以是PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED,当为后者时,读写锁可在不同进程间共享,而不局限于一个进程的不同线程间。

下面以一个例子简述读写锁的用法。

// rwlock.c
#include <pthread.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

#define handle_error_en(en, msg) \
    do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

#define MAXSIZE (10000)

struct
{
    pthread_rwlock_t rwlock;
    int buff[MAXSIZE];
    int nindex; // next index of buff
    int nvalue; // next value of buff
} shared = { PTHREAD_RWLOCK_INITIALIZER };

void* thread_write(void *arg)
{
    while (1) {
        pthread_rwlock_wrlock(&shared.rwlock);
        if (shared.nindex >= MAXSIZE) {
            printf("write thread [%ld] full\n", pthread_self());
            pthread_rwlock_unlock(&shared.rwlock);
            return NULL;
        }
        shared.buff[shared.nindex] = shared.nvalue;
        shared.nindex++;
        shared.nvalue++;
        *((int*)arg) += 1;
        pthread_rwlock_unlock(&shared.rwlock);
    }
}

void* thread_read(void *arg)
{
    int i;
    time_t start = time(NULL);
    while (1) {
        pthread_rwlock_rdlock(&shared.rwlock);
        for (i = 0; i < shared.nindex; ++i) {
            if (shared.buff[i] != i) {
                printf("read thread [%ld] error: buff[%d]=%d\n", pthread_self(), i, shared.buff[i]);
            }
        }
        *((int*)arg) += 1;
        if (shared.nindex >= MAXSIZE) {
            printf("read thread [%ld] full\n", pthread_self());
            pthread_rwlock_unlock(&shared.rwlock);
            return NULL;
        }
        if (start + 5 < time(NULL)) {
            printf("read thread [%ld] timeout\n", pthread_self());
            pthread_rwlock_unlock(&shared.rwlock);
            return NULL;
        }
        pthread_rwlock_unlock(&shared.rwlock);
    }
}

int main(int argc, char *argv[])
{
    pthread_t wrthreads[2], rdthreads[2];
    int wrcount[2], rdcount[2];
    int s, i;

    // creates write threads
    for (i = 0; i < 2; ++i) {
        wrcount[i] = 0;
        if ((s = pthread_create(&wrthreads[i], NULL, thread_write, (void*)&wrcount[i])) != 0) {
            handle_error_en(s, "pthread_create write");
        }
        else {
            printf("write thread [%ld] created, count [%d]\n", wrthreads[i], wrcount[i]);
        }
    }

    // creates read threads
    for (i = 0; i < 2; ++i) {
        rdcount[i] = 0;
        if ((s = pthread_create(&rdthreads[i], NULL, thread_read, (void*)&rdcount[i])) != 0) {
            handle_error_en(s, "pthread_create read");
        }
        else {
            printf("read thread [%ld] created, count [%d]\n", rdthreads[i], rdcount[i]);
        }
    }

    // waiting for write threads
    for (i = 0; i < 2; ++i) {
        if ((s = pthread_join(wrthreads[i], NULL)) != 0) {
            handle_error_en(s, "pthread_join write");
        }
        else {
            printf("write thread [%ld] done, count [%d]\n", wrthreads[i], wrcount[i]);
        }
    }

    // waiting for read threads
    for (i = 0; i < 2; ++i) {
        if ((s = pthread_join(rdthreads[i], NULL)) != 0) {
            handle_error_en(s, "pthread_join read");
        }
        else {
            printf("read thread [%ld] done, count [%d]\n", rdthreads[i], rdcount[i]);
        }
    }

    printf("----------done----------\n");
    exit(EXIT_SUCCESS);
}

例子中,handler_error_en用于错误处理,MAXSIZE为buff长度,shared是一个共享数据结构,包括读写锁rwlock、共享数据buff、指向buff下一个索引的nindex和指向buff下一个索引的值nvalue。main函数先创建两个线程用于写buff,再创建两个线程用于读buff,每个线程有一个计数器,创建线程前初始化为0,创建线程时作为pthread_create的参数传给对应的线程处理函数,最后主线程调用pthread_join等待读写线程结束并打印每个线程访问buff的次数。写线程thread_write函数在修改buff前,先获取写锁,buff索引每次递增1,其值与索引相同,同时增加参数arg值以记录修改buff的次数,当buff索引达到MAXSIZE时退出线程。读线程thread_read函数只是检查buff的索引是否与其值相同,每次访问buff时获取读锁,由于有两个读线程,它们的读锁可以被同时获取,这样有可能一直阻塞写线程,而buff的nindex却得不到更新,这样线程将无法结束,所以增加了延时机制,当时间超过5秒将自动退出,以给写线程机会写满buff。

gcc -o rwlock -pthread rwlock.c
./rwlock

下面是读线程超时的结果,可以看出,写线程共修改了buff MAXSIZE即10000次,是正确的,而读线程访问buff的次数却非常多,也说明了读锁可以被同时获取,最后直到超时而退出。

write thread [140007865181952] created, count [0]
write thread [140007856789248] created, count [0]
read thread [140007848396544] created, count [0]
read thread [140007840003840] created, count [0]
read thread [140007840003840] timeout
read thread [140007848396544] timeout
write thread [140007865181952] full
write thread [140007856789248] full
write thread [140007865181952] done, count [6019]
write thread [140007856789248] done, count [3981]
read thread [140007848396544] done, count [1071551]
read thread [140007840003840] done, count [1072573]
----------done----------

下面是读线程没有超时的结果,其结果也是合理的:写线程共修改buff 10000次,而读线程共访问buff的次数可能很多,也可能很少,这是不确定的。

write thread [139683703310080] created, count [0]
write thread [139683694917376] created, count [0]
read thread [139683686524672] created, count [0]
read thread [139683678131968] created, count [0]
write thread [139683694917376] full
read thread [139683678131968] full
read thread [139683686524672] full
write thread [139683703310080] full
write thread [139683703310080] done, count [0]
write thread [139683694917376] done, count [10000]
read thread [139683686524672] done, count [211941]
read thread [139683678131968] done, count [132537]
----------done----------
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值