【IPC】读写锁

来源:互联网 发布:基于效用的推荐算法 编辑:程序博客网 时间:2024/05/22 11:53

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

读写锁的数据类型为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] timeoutread thread [140007848396544] timeoutwrite thread [140007865181952] fullwrite thread [140007856789248] fullwrite 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] fullread thread [139683678131968] fullread thread [139683686524672] fullwrite thread [139683703310080] fullwrite 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