linux --- 读写锁(C++模拟实现)

在多线程中有这么一种情况,就是有着少量的写,但是却存在频繁的读取数据。
这种情况如果采取互斥锁那种方式解决的话,就会显得有点降低程序的效率
这时就可以使用读写锁来完成

适用场景

少量写 + 大量的读

  • 特点

用于不同的读取线程,可以获取读模式下的读写锁而并行的运行。

读写锁的三种状态

  • 读模式下加锁
  • 写模式下加锁
  • 不加锁

加锁规则

写的情况

  • 一次只有一个线程可以占用写模式的读写锁。
  • 一个执行流在进行写的时候,其他执行流既不能写,也不能读,只能陷入阻塞状态。

读的情况

  • 多个读取线程可以同时占用读模式下的读写锁,在读写锁的内部有一个引用计数器

引用计数:标识着有多少以读模式打开的读写锁线程

  • 每当一个线程以读模式打开读写锁的时候,引用计数器+1
  • 当一个线程释放以读模式打开的读写锁的时候,引用计数器-1

作用:判断释放读模式打开的读写锁时,能否完全解锁

  • 如果引用计数器完全减为0,表示当前没有读模式的线程占用读写锁,那么以读模式的读写锁就解锁了
  • 引用计数器大于0,表明还有线程以读模式打开读写锁,就会和想要以写模式打开的读写锁进行互斥。

在这里插入图片描述
写独占,读共享,读锁优先级高

操作接口

定义

pthread_rwlock_t

初始化

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock,
              const pthread_rwlockattr_t *restrict attr);

attr 共有 3 种选择

  • PTHREAD_RWLOCK_PREFER_READER_NP (默认设置) 读者优先,可能会导致写者饥饿情况
  • PTHREAD_RWLOCK_PREFER_WRITER_NP 写者优先,目前有 BUG,导致表现行为和
    PTHREAD_RWLOCK_PREFER_READER_NP 一致
  • PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP 写者优先,但写者不能递归加锁

加锁

  • 以读模式打开,进行加锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
  • 以写模式打开,进行加锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

解锁

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

销毁

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

完整程序

  • makefile
rwlock:rwlock.cpp
    g++ $^ -o $@ -g -lpthread -std=c++11
  • rwlock.cpp
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <vector>
#include <string>
#include <pthread.h>
#include <iostream>

using std::cout;
using std::vector;
using std::string;
using std::endl;

volatile int ticket = 1000;//保持变量ticket对内存可见性,防止编译器过度优化
pthread_rwlock_t rwlock;	//定义全局的读写锁变量
//给线程进行标号的结构体
struct ThreadAttr
{
    pthread_t tid;
    size_t id;
};

//初始化读写锁
void init_rwlock(int state)
{
    //write priority
    if(state == 0)
    {
        pthread_rwlockattr_t attr;
        pthread_rwlockattr_init(&attr);
        //定义attr变量为 写优先,但是写的时候不能递归加锁
        pthread_rwlockattr_setkind_np(&attr,PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP);
        pthread_rwlock_init(&rwlock,&attr);
        pthread_rwlockattr_destroy(&attr);
    }
    else //read priority  && write hunger
    {
        pthread_rwlock_init(&rwlock,nullptr);
    }
}

void join_threads(vector<ThreadAttr>& vec)
{
    for(auto& eoch : vec)
    {
        pthread_join(eoch.tid,nullptr);
    }
}

void* readStart(void* arg)
{
    ThreadAttr* info = (ThreadAttr*)arg;
    while(1)
    {
        pthread_rwlock_rdlock(&rwlock);
        if(ticket <= 0)
        {
            pthread_rwlock_unlock(&rwlock);
            break;
        }
        cout<<"thread reader: "<<info->id<<" : "<<ticket<<endl;
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
    return nullptr;
}

void* writeStart(void* arg)
{
    ThreadAttr* info = (ThreadAttr*) arg;
    while(1)
    {
        pthread_rwlock_wrlock(&rwlock);
        if(ticket <= 0)
        {
            pthread_rwlock_unlock(&rwlock);
            break;
        }
        cout<<"thread write "<<info->id<<" : "<<--ticket<<endl;
        pthread_rwlock_unlock(&rwlock);
        sleep(1);
    }
    return nullptr;
}

int main()
{
    const size_t reader_nr = 2;
    const size_t writer_nr = 1000;

    vector<ThreadAttr> readers(reader_nr); 
    vector<ThreadAttr> writers(writer_nr);

    init_rwlock(0);//0-->read priority 1--> write priority
    for(size_t i = 0; i < readers.size(); i++)
    {
        readers[i].id = i;
        pthread_create(&readers[i].tid,nullptr,readStart,(void*)&readers[i]);
    }

    for(size_t i = 0; i < writers.size(); i++)
    {
        writers[i].id = i;
        pthread_create(&writers[i].tid,nullptr,writeStart,(void*)&writers[i]);
    }

    //thread wait 
    join_threads(writers);
    join_threads(readers);

    pthread_rwlock_destroy(&rwlock);
    return 0;
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值