UNIX网络编程:读写锁

原创 2016年06月01日 17:13:09

之前我们整理了互斥锁与条件变量问题它保证了共享资源的安全,但在多线程中我们也会经常对共享数据进行读、写操作。也就是说对某些资源的访问会 存在两种可能的情况,一种是访问必须是排查性的,就是独占的意思,这称作写操作;另一种情况就是访问方式可以是共享的,就是说可以有多个线程同时去访问某个资源,这种就称作读操作。这个问题模型是从对文件的读写操作中引申出来的。

读写锁比起mutex具有更高的适用性,具有更高的并行性,可以有多个线程同时占用读模式的读写锁,但是只能有一个线程占用写模式的读写锁,读写锁的三种状态:
1.当读写锁是写加锁状态时,在这个锁被解锁之前,所有试图对这个锁加锁的线程都会被阻塞
2.当读写锁在读加锁状态时,所有试图以读模式对它进行加锁的线程都可以得到访问权,但是以写模式对它进行加锁的线程将会被阻塞
3.当读写锁在读模式的锁状态时,如果有另外的线程试图以写模式加锁,读写锁通常会阻塞随后的读模式锁的请求,这样可以避免读模式锁长期占用,而等待的写模式锁请求则长期阻塞。
读写锁最适用于对数据结构的读操作次数多于写操作的场合,因为,读模式锁定时可以共享,而写模式锁定时只能某个线程独占资源,因而,读写锁也可以叫做个共享-独占锁。
处理读-写问题的两种常见策略是强读者同步和强写者同步
强读者同步中,总是给读者更高的优先权,只要写者当前没有进行写操作,读者就可以获得访问权限;而在强写者同步中,则往往将优先权交付给写者,而读者只能等到所有正在等待的或者是正在执行的写者结束以后才能执行。系统中的读写锁时用的强写者,这是为了避免再修改数据时,读操作先执行导致读出的数据无效。

下面分别是强读者和强写者的函数实现代码

强写者实现代码::
pthread_rwlock.h:

#pragma once                  //只编译一次
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>

typedef struct
{
    pthread_mutex_t rw_mutex;         //定义一个读写互斥量
    pthread_cond_t  rw_condreaders;   //定义一个读者条件变量
    pthread_cond_t  rw_condwriters;   //定义一个写着条件变量
    int             rw_magic;         //定义一个魔术值,来标记是否空闲
    int             rw_nwaitwriters;  //定义一个读等待标记等待读的线程数
    int             rw_nwaitreaders;  //定义一个写等待标记等待写的线程数 
    int             rw_refcount;      //标记现在所使用的个数,只能为  -1(代表有线程进行写操作)、0(没有线程对共享数据进行读写操作)、
                                      //>0(代表现在进行读操作的线程数,因为可以同时多个线程进行读操作,所以可以一个大于0的整数)。
}my_pthread_rwlock_t;               

#define RW_MAGIC   0x19283746         //初始魔数值

//初始化my_pthread_rwlock_t。
#define
PTHREAD_RWLOCK_INITIALIZER{PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER,\
PTHREAD_COND_INITIALIZER, RW_MAGIC, 0, 0, 0};

typedef int my_pthread_rwlockattr_t;

int my_pthread_rwlock_init(my_pthread_rwlock_t *, my_pthread_rwlockattr_t *);      //读写锁初始化函数
int my_pthread_rwlock_rdlock(my_pthread_rwlock_t *rw);   //读锁函数
int my_pthread_rwlock_wrlock(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);  //尝试上写锁
int my_pthread_rwlock_unlock(my_pthread_rwlock_t *rw);  //解锁函数
int my_pthread_rwlock_destroy(my_pthread_rwlock_t *rw);  //销毁读写锁

pthread_rwlock.cpp:

#include "pthread_rwlock.h"

//读写锁初始化函数
int my_pthread_rwlock_init(my_pthread_rwlock_t *rw, my_pthread_rwlockattr_t *attr)
{
    int res;
    if(attr != NULL){       //如果创建属性不为NULL,就返回不可用
        return(EINVAL);
    }
    if(res = pthread_mutex_init(&rw->rw_mutex, NULL) != 0){            //初始化互斥锁,如果成功则返回0,否则初始化互斥锁失败
        goto err1;      //初始化失败调转到err1处理情况
    }    
    if(res = pthread_cond_init(&rw->rw_condreaders, NULL) != 0){        //初始化读条件变量,如果成功则返回0,否则初始化读条件变量失败
        goto err2;    //初始化失败后调转到err2处理方法
    }
    if(res = pthread_cond_init(&rw->rw_condwriters, NULL) != 0){       //初始化写条件变量,如果成功返回0, 
        goto err3;   //初始化失败后跳转到err3处理方法。
    }

    rw->rw_nwaitreaders = 0;      //初始化等待读操作的线程数为0
    rw->rw_nwaitwriters = 0;      //初始化等待写操作的线程数为0
    rw->rw_refcount = 0;          //初始化正在操作共享数据的线程数为0
    rw->rw_magic = RW_MAGIC;      //将初始值赋给检验变量魔数

    return 0;

    err3:  //因为执行err3是创建了读条件变量和互斥锁,锁以销毁读条件变量,然后顺序执行进入err2销毁互斥量,
           //最后顺序执行err1,返回错误参数。这就是如此设计的原因。
        pthread_cond_destroy(&rw->rw_condreaders);
    err2:  //如果是直接跳入err2的,那此时就只有互斥量了,直接销毁它然后返回错误参数。
        pthread_mutex_destroy(&rw->rw_mutex);
    err1:  //此时还没有创建成功任何变量或所创建变量已在err3和err2中销毁,直接返回错误参数。
        return res;
}

//销毁读写锁函数
int my_pthread_rwlock_destroy(my_pthread_rwlock_t *rw)
{
    if(rw->rw_magic != RW_MAGIC){       //如果rw_magic不等于初始值,说明有线程在使用,直接返回
        return EINVAL;
    }
    //如果有线程等待执行读操作或有线程在等待执行写操作,则返回EBUSY错误,并结束函数
    if(rw->rw_nwaitreaders != 0 || rw->rw_nwaitwriters != 0 || rw->rw_refcount != 0){   
        return  EBUSY;
    }
    //如果rw_magic为初始值,且没有线程再等待读操作或写操作时,将互斥量、读条件变量、写条件变量分别销毁
    pthread_mutex_destroy(&rw->rw_mutex);
    pthread_cond_destroy(&rw->rw_condreaders);
    pthread_cond_destroy(&rw->rw_condwriters);

    rw->rw_magic = 0;   //最后将rw_magic置为0

    return 0;
}

//读锁的实现函数
int my_pthread_rwlock_rdlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
    //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if((res = pthread_mutex_lock(&rw->rw_mutex)) != 0){
        return res;
    }
    //查看rw_refcount的值看是否小于0(即是否有线程对其进行写操作),或是否有线程正在等待对其进行写操作,
    //如果有则将rw_nwaitreaders值加1,并将程序阻塞等待读条件变量后在对其上所进行读操作
    //(因为此程序实现的是写优先,所以要等到没有写操作线程或等待写操作的线程时,才能让进行读的线程获得资源)
    while(rw->rw_refcount < 0 || rw->rw_nwaitwriters > 0){
        rw->rw_nwaitreaders++;
        res = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex);
        if(res != 0){
            break;
        }
    }
    //如果读锁加成功后,给refcount加1,因为正数值代表的时进行读操作的线程个数
    if(res == 0){
        rw->rw_refcount++;
    }
    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,
    //保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unlock(&rw->rw_mutex);
    return res;
}

//写操作上锁函数
int my_pthread_rwlock_wrlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
     //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if(res = pthread_mutex_lock(&rw->rw_mutex) != 0){
        return res;
    }

//查看rw_refcount的值看是否为0(即是否有线程对其进行操作),如果rw_refcount不为0,则将rw_nwaitwriters值加1,
//并将程序阻塞等待接收到写条件变量后再对其上所进行读操作(因为此程序实现的是写优先,所以只要没有线程操作时,就让该线程进行写操作)
    while(rw->rw_refcount != 0){
        rw->rw_nwaitwriters ++;
        res = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);
        rw->rw_nwaitwriters--;
        if(res != 0){
            break;
        }
    }
    //如果读锁加成功后,给refcount置为-1,因为-1代表写操作(因为只能同时有且仅有一个线程进行写操作,所以,加读锁后rw_refcount只能为-1)
    if(res == 0){
        rw->rw_refcount = -1;
    }
    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unlock(&rw->rw_mutex);

    return res;
}

int my_pthread_rwlock_tryrdlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
    //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if((res = pthread_mutex_lock(&rw->rw_mutex)) != 0){
        return res;
    }
    //此处是尝试上所函数中读写优先的不同地方。
    //如果有写线程正在执行或有写线程在等待,则返回EBUSY
    if(rw->rw_refcount < 0 || rw->nwaitwriters > 0){
        res = EBUSY; 
    }else{    //否则,让rw_refcount加1,让其准确的记录当前正在执行的读进程个数
        rw->rw_refcount ++;
    } 
    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,
    //保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unclock(&rw->rw_mutex);
    return res;
}

int my_pthread_rwlock_trywrlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   
    //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
    //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if((res = pthread_mutex_lock(&rw->rw_mutex)) != 0){
        return res;
    }
    //如果正在操作的线程数不为0,则返回EBUSY
    if(rw->rw_refcount != 0){
        res = EBUSY; 
    }else{    //否则,让rw_refcount = -1,让其状态为有写进程操作
        rw->rw_refcount = -1;
    } 
    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,
    //保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unclock(&rw->rw_mutex);
    return res;
}
//解锁函数
int my_pthread_rwlock_unlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){  
    //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
    //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if((res = pthread_mutex_lock(&rw->rw_mutex)) != 0){
        return res;
    } 
    //如果此时rw_refcount大于0,所以现在再执行的操作为读操作,给rw_refcount减1,解除读线程的锁
    if(rw->rw_refcount > 0){
        rw->rw_refcount--;
    }else if(rw->rw_refcount == -1){  //如果rw_refcount = -1,说明执行的是写操作,将其值赋为0,解除写线程的锁
        rw->rw_refcount = 0;
    }else{
        rw->rw_refcount = 0;
    }
//此处是写着优先和读者优先实现的第二处不同
    if(rw->rw_nwaitwriters > 0){    //当等待写操作的线程数不为0
        if(rw->rw_refcount == 0){   //rw_refcount正在执行的线程数为0
        //发送写条件变量,通知写线程有空闲资源
            res = pthread_cond_signal(&rw->rw_condwriters);
        }
    }else if(rw->rw_nwaitreaders > 0){  
    //再没有写线程等待操作且有可用资源的前提下,广播发送读条件变量,通知所有等待读操作的线程有空闲资源
        res = pthread_cond_broadcast(&rw->rw_condreaders);
    }
     //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,
     //保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unlock(&rw->rw_mutex);  
    return res;
}

读者优先程序代码:
pthread_rwlock.cpp:

#include "pthread_rwlock.h"

//读写锁初始化函数
int my_pthread_rwlock_init(my_pthread_rwlock_t *rw, my_pthread_rwlockattr_t *attr)
{
    int res;
    if(attr != NULL){       //如果创建属性不为NULL,就返回不可用
        return(EINVAL);
    }
    if(res = pthread_mutex_init(&rw->rw_mutex, NULL) != 0){            /初始化互斥锁,如果成功则返回0,否则初始化互斥锁失败
        goto err1;      //初始化失败调转到err1处理情况
    }    
    if(res = pthread_cond_init(&rw->rw_condreaders, NULL) != 0){        //初始化读条件变量,如果成功则返回0,否则初始化读条件变量失败
        goto err2;    //初始化失败后调转到err2处理方法
    }
    if(res = pthread_cond_init(&rw->rw_condwriters, NULL) != 0){       //初始化写条件变量,如果成功返回0, 
        goto err3;   //初始化失败后跳转到err3处理方法。
    }

    rw->rw_nwaitreaders = 0;      //初始化等待读操作的线程数为0
    rw->rw_nwaitwriters = 0;      //初始化等待写操作的线程数为0
    rw->rw_refcount = 0;          //初始化正在操作共享数据的线程数为0
    rw->rw_magic = RW_MAGIC;      //将初始值赋给检验变量魔数

    return 0;

    err3:  //因为执行err3是创建了读条件变量和互斥锁,锁以销毁读条件变量,然后顺序执行进入err2销毁互斥量,
           //最后顺序执行err1,返回错误参数。这就是如此设计的原因。
        pthread_cond_destroy(&rw->rw_condreaders);
    err2:  //如果是直接跳入err2的,那此时就只有互斥量了,直接销毁它然后返回错误参数。
        pthread_mutex_destroy(&rw->rw_mutex);
    err1:  //此时还没有创建成功任何变量或所创建变量已在err3和err2中销毁,直接返回错误参数。
        return res;
}

//销毁读写锁函数
int my_pthread_rwlock_destroy(my_pthread_rwlock_t *rw)
{
    if(rw->rw_magic != RW_MAGIC){       //如果rw_magic不等于初始值,说明有线程在使用,直接返回
        return EINVAL;
    }
    //如果有线程等待执行读操作或有线程在等待执行写操作,则返回EBUSY错误,并结束函数
    if(rw->rw_nwaitreaders != 0 || rw->rw_nwaitwriters != 0 || rw->rw_refcount != 0){   
        return  EBUSY;
    }
    //如果rw_magic为初始值,且没有线程再等待读操作或写操作时,将互斥量、读条件变量、写条件变量分别销毁
    pthread_mutex_destroy(&rw->rw_mutex);
    pthread_cond_destroy(&rw->rw_condreaders);
    pthread_cond_destroy(&rw->rw_condwriters);

    rw->rw_magic = 0;   //最后将rw_magic置为0

    return 0;
}

//读锁的实现函数
int my_pthread_rwlock_rdlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
    //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if((res = pthread_mutex_lock(&rw->rw_mutex)) != 0){
        return res;
    }
    //查看rw_refcount的值看是否小于0(即是否有线程对其进行写操作),如果有写线程正在执行,则将rw_nwaitreaders值加1,
    //并将程序阻塞等待读条件变量后在对其上所进行读操作
    //(因为此程序实现的是读优先,所以只要没有写线程正在执行,就让进行读的线程获得资源)
    while(rw->rw_refcount < 0){
        rw->rw_nwaitreaders++;
        res = pthread_cond_wait(&rw->rw_condreaders, &rw->rw_mutex);
        if(res != 0){
            break;
        }
    }
    //如果读锁加成功后,给refcount加1,因为正数值代表的时进行读操作的线程个数
    if(res == 0){
        rw->rw_refcount++;
    }
    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unlock(&rw->rw_mutex);
    return res;
}

//写操作上锁函数
int my_pthread_rwlock_wrlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   
    //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
     //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if(res = pthread_mutex_lock(&rw->rw_mutex) != 0){
        return res;
    }

     //查看rw_refcount的值看是否为0(即是否有线程对其进行操作),如果rw_refcount不为0或有读线程在等待,
     //则将rw_nwaitwriters值加1,并将程序阻塞等待接收到写条件变量后再对其上锁进行读操作
     //(因为此程序实现的是读优先,所以只有当没有线程操作且没有读线程等待时,才能让写线程获得资源进行写操作)
    while(rw->rw_refcount != 0 || rw->rw_nwaitreaders > 0){
        rw->rw_nwaitwriters ++;
        res = pthread_cond_wait(&rw->rw_condwriters, &rw->rw_mutex);
        rw->rw_nwaitwriters--;
        if(res != 0){
            break;
        }
    }
    //如果读锁加成功后,给refcount置为-1,因为-1代表写操作
    //(因为只能同时有且仅有一个线程进行写操作,所以,加读锁后rw_refcount只能为-1)
    if(res == 0){
        rw->rw_refcount = -1;
    }
    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unlock(&rw->rw_mutex);

    return res;
}

int my_pthread_rwlock_tryrdlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   
    //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
    //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if((res = pthread_mutex_lock(&rw->rw_mutex)) != 0){
        return res;
    }
    //此处是尝试上所函数中读写优先的不同地方。
    //如果有写线程正在执行,则返回EBUSY
    if(rw->rw_refcount < 0){
        res = EBUSY; 
    }else{    //否则,让rw_refcount加1,让其准确的记录当前正在执行的读进程个数
        rw->rw_refcount ++;
    } 
    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unclock(&rw->rw_mutex);
    return res;
}

int my_pthread_rwlock_trywrlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
    //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if((res = pthread_mutex_lock(&rw->rw_mutex)) != 0){
        return res;
    }
    //如果正在操作的线程数不为0或等待读操作的线程个数不为0,则返回EBUSY
    if(rw->rw_refcount != 0 || rw->nwaitreaders > 0){
        res = EBUSY; 
    }else{    //否则,让rw_refcount = -1,让其状态为有写进程操作
        rw->rw_refcount = -1;
    } 
    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unclock(&rw->rw_mutex);
    return res;
}
//解锁函数
int my_pthread_rwlock_unlock(my_pthread_rwlock_t *rw)
{
    int res;
    if(rw->rw_magic != RW_MAGIC){   
    //如果rw_magic不为初始值说明有线程正在进行操作,直接返回EINVAL错误并结束函数
        return EINVAL;
    }
    //无论何时操作my_pthread_rwlock结构体都要对rw_mutex成员上锁
    if((res = pthread_mutex_lock(&rw->rw_mutex)) != 0){
        return res;
    } 

    if(rw->rw_refcount == -1){  //如果rw_refcount = -1,说明执行的是写操作,将其值赋为0,解除写线程的锁
        rw->rw_refcount = 0;
    }else if(rw->rw_refcount > 0){  //如果此时rw_refcount大于0,所以现在再执行的操作为读操作,给rw_refcount减1,解除读线程的锁
        rw->rw_refcount--;
    }else{
        rw->rw_refcount = 0;
    } 
    if(rw->rw_nwaitreaders > 0){  //如果有读线程等待则广播发送读条件变量,通知所有等待读操作的线程有空闲资源
        res = pthread_cond_broadcast(&rw->rw_condreaders);
    }else if(rw->rw_nwaitwriters > 0){  //如果没有读线程等待且等待写操作的线程数不为0时,
//发送写条件变量,通知写线程有空闲资源
        if(rw->rw_refcount == 0){
            res = pthread_cond_signal(&rw->rw_condwriters);
        }
    }    

    //对控制my_pthread_rwlock结构体操作的rw_mutex成员解锁,保证让其他线程可以操作my_pthread_rwlock结构体
    pthread_mutex_unlock(&rw->rw_mutex);  
    return res;
}

测试代码:
test.cpp:

#include<unistd.h>
#include<stdio.h>
#include<pthread.h>
#include "pthread_rwlock.h"

my_pthread_rwlock_t rwlock;
//
void* thread_fun1(void *arg)
{
    my_pthread_rwlock_wrlock(&rwlock);
    printf("This is fun1.\n");
    sleep(5);
    my_pthread_rwlock_unlock(&rwlock);
}
void* thread_fun2(void *arg)
{
    printf("thread2 is run......\n");
    my_pthread_rwlock_rdlock(&rwlock);
    printf("This is fun2.\n");
    my_pthread_rwlock_unlock(&rwlock);
}
void* thread_fun3(void *arg)
{
    printf("thread3 is run......\n");
    my_pthread_rwlock_rdlock(&rwlock);
    printf("This is fun3.\n");
    my_pthread_rwlock_unlock(&rwlock);
}
void* thread_fun4(void *arg)
{
    printf("thread4 is run......\n");
    my_pthread_rwlock_wrlock(&rwlock);
    printf("This is fun4.\n");
    my_pthread_rwlock_unlock(&rwlock);
}

int main()
{
    my_pthread_rwlock_init(&rwlock, NULL);
    pthread_t tid[4];
    pthread_create(&tid[0], NULL, thread_fun1, NULL);
    sleep(1);
    pthread_create(&tid[1], NULL, thread_fun2, NULL);
    pthread_create(&tid[2], NULL, thread_fun3, NULL);
    pthread_create(&tid[3], NULL, thread_fun4, NULL);

    pthread_join(tid[0], NULL);
    pthread_join(tid[1], NULL);
    pthread_join(tid[2], NULL);
    pthread_join(tid[3], NULL);
    my_pthread_rwlock_destroy(&rwlock);
    return 0;
}

运行结果:
用写者优先程序测试结果:
这里写图片描述

用读者优先程序测试结果:
这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。 举报

相关文章推荐

linux网络编程之POSIX信号量、自旋锁、读写锁

posix信号量 system v 信号量只能用于进程间同步,而posix信号量除了可以进程间同步,还可以线程间同步。systemv 信号量每次PV操作可以是N,但Posix信号量每次PV只能是1。除...

Unix环境,文件读写锁

1简介:    做个假设,如果有多个进程,共同编辑一个文件,那个这个文件安最后的结果是什么?在普通的Unix环境下,并没有限制多个进程共同读写一个文件。但是,如果这种情况出现在数据库中,怎么办。数据库...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

Linux系统编程——线程同步与互斥:读写锁

读写锁基本原理 当有一个线程已经持有互斥锁时,互斥锁将所有试图进入临界区的线程都阻塞住。但是考虑一种情形,当前持有互斥锁的线程只是要读访问共享资源,而同时有其它几个线程也想读取这个共享资源,但是...

java并发编程——读写锁

读写锁 同时允许多个线程获取这个锁 ReentrantLock这种排他锁一个时刻只允许一个线程获取锁,而读写锁同一个时刻允许多个读取操作线程获取锁。但是对于写锁,只允许一个写操作的线程排他的获取锁。...

多线程编程 RW_LOCK 读写锁

RW锁 读写锁,也叫共享独占锁  互斥量 要么是锁住状态,要么是不加锁状态,而且一次只有一个线程可以对其加锁。 读写锁可以有三种状态,读模式下加锁状态,写模式下加锁状态,不加锁状态。一次只有一...

Linux系统编程——读写锁

读写锁 (rwlock)功能特点简介: 读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,...

并发编程--读写锁ReadWriteLock和ReentrantReadWriteLock(一)

Java并发编程包提供了读写锁的实现,其维护了一对相关的锁 — — “读取锁”和“写入锁”,一个用于读取操作,另一个用于写入操作。“读取锁”用于只读操作,它是“共享锁”,能同时被多个线程获取。“写入锁...

多线程编程之自旋锁,读写锁

自旋锁相对于原子锁可以实现更为复杂的功能

java并发编程系列之ReadWriteLock读写锁的使用

前面我们讲解了Lock的使用,下面我们来讲解一下ReadWriteLock锁的使用,顾明思义,读写锁在读的时候,上读锁,在写的时候,上写锁,这样就很巧妙的解决synchronized的一个性能问题:读...

并发编程--使用读写锁实现同步数据访问

锁机制最大的改进之一就是ReadWriteLock接口和
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)