UNIX网络编程:读写锁

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

读写锁比起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;
}

运行结果:

用写者优先程序测试结果:

用读者优先程序测试结果:

时间: 2024-10-08 21:57:56

UNIX网络编程:读写锁的相关文章

[转载] 读《UNIX网络编程 卷1:套接字联网API》

原文: http://cstdlib.com/tech/2014/10/09/read-unix-network-programming-1/ 文章写的很清楚, 适合初学者 最近看了<UNIX网络编程 卷1:套接字联网API>, 英文名叫Unix Network Programming啦,后来上网查了查, 一般都叫UNP逼格会高一点, 就像APUE一样. 他们的作者都是W. Richard Stevens. 另外,他也是TCP/IP Illustrated的作者. 靠,看完作者简介,简直崇拜得

UNIX网络编程 卷2:进程间通信

这篇是计算机类的优质预售推荐>>>><UNIX网络编程 卷2:进程间通信(第2版)> UNIX和网络专家W. Richard Stevens的传世之作 编辑推荐 两卷本的<UNIX网络编程>是已故著名技术作家W. Richard Stevens的传世之作.卷2着重讨论怎样让应用程序与在其它机器上的应用程序进行对话. 良好的进程间通信(IPC)机制是提高UNIX程序性能的关键. 本书全面深入地解说了各种进程间通信形式,包括消息传递.同步.共享内存及远程过程调用

Unix网络编程-同步

1.互斥锁(量)和条件变量 默认情况下互斥锁和条件变量用于线程间同步,若将它们放在共享内存区,也能用于进程间同步. 1.1 互斥锁 1.概述: 互斥锁(Mutex,也称互斥量),防止多个线程对一个公共资源做读写操作的机制,以保证共享数据的完整性. 用以保护临界区,以保证任何时候只有一个线程(或进程)在访问共享资源(如代码段).保护临界区的代码形式: lock_the_mutex(...); 临界区 unlock_the_mutex(...); 任何时刻只有一个线程能够锁住一个给定的互斥锁. 下面

UNIX网络编程卷1 回射客户程序 TCP客户程序设计范式

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 下面我会介绍同一个使用 TCP 协议的客户端程序的几个不同版本,分别是停等版本.select 加阻塞式 I/O 版本. 非阻塞式 I/O 版本.fork 版本.线程化版本.它们都由同一个 main 函数调用来实现同一个功能,即回射程序客户端. 它从标准输入读入一行文本,写到服务器上,读取服务器对该行的回射,并把回射行写到标准输出上. 其中,非阻塞式 I/O 版本是所有版本中执行速度最快的,

Unix网络编程中的五种I/O模型_转

转自:Unix网络编程中的的五种I/O模型 下面主要是把unp第六章介绍的五种I/O模型. 1. 阻塞I/O模型 例如UDP函数recvfrom的内核到应用层.应用层到内核的调用过程是这样的:首先把描述符.接受数据缓冲地址.大小传递给内核,但是如果此时 该与该套接口相应的缓冲区没有数据,这个时候就recvfrom就会卡(阻塞)在这里,知道数据到来的时候,再把数据拷贝到应用层,也就是传进来的地址空 间,如果没有数据到来,就会使该函数阻塞在那里,这就叫做阻塞I/O模型,如下图: 2. 非阻塞I/O模

Unix网络编程 之 socket基础

基本结构 (这部分的地址均为网络地址<网络字节序>) 1.struct sockaddr:通用套接字地址结构 此结构用于存储通用套接字地址. 数据结构定义: typedef unsigned short sa_family_t; struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */ };    sa_fa

【UNIX网络编程】FIFO

管道作为进程间通信的最古老方式,它的缺点是没有名字,因此只能用在有亲缘关系的父子进程之间.对于无亲缘关系的进程间,无法用管道进行通信.FIFO可以完成无亲缘关系的进程间的通信,FIFO也被称为命名管道.它是一种特殊类型的文件,在文件系统中以文件名的形式存在,但它的行为却和上面提到的管道类似. 创建命名管道有两种方法: 1.在命令行上执行命令:mkfifo filename 来创建FIFO. 2.使用mkfifo函数创建FIFO. #include <sys/stat.h> #include &

【UNIX网络编程】进程间通信之管道

管道是最早的Unix进程间通信形式,它存在于全部的Unix实现中.关于管道,有例如以下几点须要知道: 1.它是半双工的,即数据仅仅能在一个方向上流动.虽然在某些Unix实现中管道能够是全双工的.但须要对系统进行某些设置.在Linux系统中,它是半双工的. 2.它没有名字.因此仅仅能在具有公共祖先的进程之间使用. 通经常使用在父子进程间.虽然这一点随着"有名管道FIFO"的增加得到改正了.但应该把它们看作是两种不同的进程间通信方式. 3.它由pipe函数创建,read和write函数訪问

UNIX网络编程——网络I/O模型

在学习UNIX网络编程的时候.一開始分不清 同步 和 异步,所以还是总结一下,理清下他们的差别比較好. IO分类 IO依据对IO的调度方式可分为堵塞IO.非堵塞IO.IO复用.信号驱动IO.异步IO. IO操作整个流程分为 可操作推断 和 实际IO操作 两个区间,我们能够称之为两个半程,前半程推断是否可操作,后半程进行实际操作. 当中堵塞IO.非堵塞IO.IO复用.信号驱动IO由于其[实际的IO操作是同步堵塞]的,所以一般把他们归为同步IO,异步IO的实际IO操作是在独立的线程中完毕的,所以称为

UNIX网络编程-recv、send、read、write之间的联系与区别

1.read ----------------------------------------------------------------------- #include <unistd.h> ssize_t read(int fd, void *buf, size_t nbyte); ----------------------------------------------------------------------- read()函数是负责从fd中读取内容.当读成功时,read(