读写锁

 读写锁的分配规则:

(1)只要没有线程持有某个给定的读写锁用于写,那么任意数目的线程可以持有该读写锁用于读。

(2)仅当没有线程持有某个给定的读写锁用于读或者写时,才能分配该读写锁用于写。

即只要没有线程在修改某个给定的数据,那么任意数目的线程都可以拥有该数据的读访问权。仅当没有其它线程在

读或修改某个给定的数据时,当前线程才可以修改它。

   这种对于某个给定资源的共享访问也称为共享-独占上锁,因为获取一个读写锁用于读称为共享锁,获取一个读写

锁用于写称为独占锁。

   读写锁的数据类型为pthread_rwlock_t。如果这个类型的某个变量是静态分配的,那么可通过给它赋常值PTHREAD

_RWLOCK_INITIALIZER来初始化它。下面是定义了基本的pthread_rwlock_t数据类型和操作读写锁的各个函数原型:

#pragma once

#include<pthread.h>
#include<stdio.h>

typedef struct
{
    pthread_mutex_t rw_mutex;
    pthread_cond_t  rw_condreaders;
    pthread_cond_t  rw_condwriters;
    int             rw_magic;
    int             rw_nwaitreaders;
    int             rw_nwaitwriters;
    int             rw_refcount;    
}my_pthread_rwlock_t;

#define  RW_MAGIC  0x20180501
#define MY_PTHREAD_RWLOCK_INITIALIZER {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, PTHREAD_COND_INITIALIZER,RW_MAGIC,0,0,0}

#define EBUSY 3
typedef int  my_pthread_rwlockattr_t;
int my_pthread_rwlock_destroy(my_pthread_rwlock_t *rw);
int my_pthread_rwlock_rdlock(my_pthread_rwlock_t *rw);
int my_pthread_rwlock_wrlock(my_pthread_rwlock_t *rw);
int my_pthread_rwlock_unlock(my_pthread_rwlock_t *rw);
int my_pthread_rwlock_tryrdlock(my_pthread_rwlock_t *rw);
int my_pthread_rwlock_trywrlock(my_pthread_rwlock_t *rw);

  在my_pthread_rwlock_t数据类型中包含一个互斥锁、两个条件变量、一个标志以及三个计数器。无论何时检查或

操作该结构,我们都必须持有其中的互斥锁成员rw_mutex,即都必须给其中的rw_mutex成员上锁。该结构初始化成

功后,标志成员rw_magic就被设置成RW_MAGIC。所有函数都测试该成员,以检查调用者是否向某个已初始化的读

写锁传递了指针。该读写锁被摧毁时,这个成员就被置为0。计数器成员之一的rw_refcount总是指示着本读写锁的

当前状态:-1表示它是一个写入锁(任意时刻这样的锁只能有一个),0表示它是可用的,大于0的值则意味着它当

前容纳着那么多的读写锁。

   my_pthread_rwlock_rdlock函数:

int my_pthread_rwlock_rdlock(my_pthread_rwlock_t *rw)
{
    int result;
    if(rw->rw_magic != RW_MAGIC)
        return -1;
    if((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
        return result;

    while(rw->rw_refcount< 0 || rw->rw_nwaitwriters> 0)
    {
        rw->rw_nwaitreaders++;
        result = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex);
        rw->rw_nwaitreaders--;
        if(result != 0)
            break;
    }
    if(result == 0)
        rw->rw_refcount++;
    pthread_mutex_unlock(&rw->rw_mutex);
    return result;
}

  如果rw_refcount小于0(意味着当前有一个写入者持有由调用者指定的读写锁),或者有线程正等着获取该读写锁

的一个写入锁(rw_nwaitwriters大于0),那么我们无法获取该读写锁的一个读出锁。如果这两个条件中有一个为真

,我们就把rw_nwaitreaders加1。并在rw_condreaders条件变量上调用pthread_cond_wait。当给一个读写锁解锁时

,首先检查是否有任何等待着的写入者,若没有则检查是否有任何等待着的读出者。如果有读出者在等待,那就向

rw_condreaders条件变量广播信号。取得读出锁后把rw_refcount加1,互斥锁即释放。

   my_pthread_rwlock_tryrdlock函数:

int my_pthread_rwlock_tryrdlock(my_pthread_rwlock_t *rw)
{
    int result;
    if(rw->rw_magic!= RW_MAGIC)
        return -1;
    if((result= pthread_mutex_lock(&rw->rw_mutex))!= 0)
        return result;
    if(rw->rw_refcount< 0||rw->rw_nwaitwriters> 0)
        result= EBUSY;
    else
        rw->rw_refcount++;
    pthread_mutex_unlock(&rw->rw_mutex);
    return result;
}

   该函数它在尝试获取一个读出锁时并不阻塞。如果当前有一个写入者持有调用者指定的读写锁,或者有线程在等待

该读写锁的一个写入锁,那就返回EBUSY错误。否则,通过把rw_refcount加1获取该读写锁。

    my_pthread_rwlock_wrlock函数:

int my_pthread_rwlock_wrlock(my_pthread_rwlock_t *rw)
{
    int result;
    if(rw->rw_magic != RW_MAGIC)
        return -1;

    if((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
        return result;

    while(rw->rw_refcount != 0)
    {
        rw->rw_nwaitwriters++;
        result = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);
        rw->rw_nwaitwriters--;
        if(result != 0)
            break;
    }
    if(result == 0)
        rw->rw_refcount = -1;

    pthread_mutex_unlock(&rw->rw_mutex);
    return result;
}

   只要有读出者持有由调用者指定的读写锁的读出锁,或者有一个写入者持有该读写锁的唯一写入锁(两者都是rw_re

fcount不为0的情况),调用线程就得阻塞。为此,我们把rw_nwaitwriters加1,然后在rw_condwriters条件变量上调用

pthread_cond_wait。向条件变量发送信号的前提是:它所在的读写锁被释放,并且有写入者正在等待。取得写入锁后

把rw_refcount置为-1。

   my_pthread_rwlock_trywrlock函数:

int my_pthread_rwlock_trywrlock(my_pthread_rwlock_t *rw)
{
    int result;
    if(rw->rw_magic!= RW_MAGIC)
        return -1;
    if((result= pthread_mutex_lock(&rw->rw_mutex))!= 0)
        return result;
    if(rw->rw_refcount!= 0)
        result= EBUSY;
    else
        rw->rw_refcount= -1;
    pthread_mutex_unlock(&rw->rw_mutex);
    return result;
}

   如果rw_refcount不为0,那么由调用者指定的读写锁或者由一个写入者持有,或者一个或多个读出者持有,因为返回

一个EBUSY错误。否则,获取该读写锁的写入锁,并把rw_refcount置为-1。

   my_pthread_rwlock_unlock函数:

int my_pthread_rwlock_unlock(my_pthread_rwlock_t *rw)
{
    int result;
    if(rw->rw_magic != RW_MAGIC)
        return -1;
    if((result = pthread_mutex_lock(&rw->rw_mutex)) != 0)
        return result;

    if(rw->rw_refcount > 0)
        rw->rw_refcount--;
    else if(rw->rw_refcount == -1)
        rw->rw_refcount = 0;
    else
        printf("unlock error.\n");

    if(rw->rw_nwaitwriters > 0)
    {
        if(rw->rw_refcount == 0)
        {
            result = pthread_cond_signal(&rw->rw_condwriters);
        }
    }
    else if(rw->rw_nwaitreaders > 0)
        result = pthread_cond_broadcast(&rw->rw_condreaders);

    pthread_mutex_unlock(&rw->rw_mutex);
    return result;
}

   如果rw_refcount当前大于0,那么有一个读出者(即调用线程)准备释放一个读出锁。如果rw_refcount当前为-1,那

么一个写入者(即调用线程)准备释放一个写入锁。如果有一个写入者在等待,那么一旦由调用者指定的读写锁变得可

用(即它的引用计数变为0),就向rw_condwriters条件变量发送信号。只有一个写入者能够获取该读写锁,因此调用pt

hread_cond_signal来唤醒一个线程。如果没有写入者在等待,但是有一个或多个读出者在等待,那就在rw_condreaders

条件变量上调用pthread_cond_broadcast,因为所有等待着的读出锁都可以获取一个读出锁。一旦有一个写入者在等待

,我们就不给任何读出者授予读出锁,否则一个持续的读请求流可能永远阻塞某个等待着的写入者。

   my_pthread_rwlock_destroy函数:

int my_pthread_rwlock_destroy(my_pthread_rwlock_t *rw)
{
    if(rw->rw_magic!= RW_MAGIC)
        return -1;
    if(rw->rw_refcount!= 0||rw->rw_nwaitreaders!= 0||rw->rw_nwaitwriters!= 0)
        return(EBUSY);
    pthread_mutex_destroy(&rw->rw_mutex);
    pthread_cond_destroy(&rw->rw_condreaders);
    pthread_cond_destroy(&rw->rw_condwriters);
    rw->rw_magic= 0;

    return 0;
}

   该函数在所有线程(包括调用者在内)都不再持有也不试图持有某个读写锁的时候摧毁该锁。首先检查由调用者指定

的读写锁已不再使用中,然后给其中的互斥锁和两个条件变量成员调用合适的摧毁函数。

posted @ 2018-06-14 16:06  XNQC  阅读(286)  评论(0编辑  收藏  举报