【线程】读写锁

一、概念

一把读写锁具备三种状态:

  • 读模式下加锁状态(读锁)
  • 写模式下加锁转态(写锁)
  • 不加锁状态

2. 读写锁特性:

  • 读写锁是写模式加锁时,解锁前,所有对该锁加锁的线程都会阻塞。
  • 读写锁是读模式加时,如果线程以读模式加锁会成功,如果线程以写模式加锁会阻塞
  • 读写锁是读模式加锁时,既有试图以写模式加锁的线程,也有试图以读模式加锁的线程,那么读模式会读阻塞随后的读模式锁请求,优先满足写模式锁,读锁、写锁并行阻塞,写锁优先级高

读写锁也叫共享_独占锁,当读写锁以读模式锁住时,它以共享模式锁住;当它以写模式锁住时,它是独占锁住的,写独占,读共享


3. 读写锁的特性:

线程A加读锁成功, 又来了三个线程, 做读操作, 可以加锁成功

  • 读共享 - 并行处理

线程A加写锁成功, 又来了三个线程, 做读操作, 三个线程阻塞

  • 写独占

线程A加读锁成功, 又来了B线程加写锁阻塞, 又来了C线程加读锁阻塞

  • 读写不能同时
  • 写的优先级高

4. 读写锁场景:

线程A加写锁成功, 线程B请求读锁

  • 线程B阻塞

线程A持有读锁, 线程B请求写锁

  • 线程B阻塞

线程A拥有读锁, 线程B请求读锁

  • 线程B加锁成功

线程A持有读锁, 然后线程B请求写锁, 然后线程C请求读锁

  • B阻塞,c阻塞 - 写的优先级高
  • A解锁,B线程加写锁成功,C继续阻塞
  • B解锁,C加读锁成功

线程A持有写锁, 然后线程B请求读锁, 然后线程C请求写锁

  • BC阻塞
  • A解锁,C加写锁成功,B继续阻塞
  • C解锁,B加读锁成功

二、主要函数

1. 函数原型:

#include <pthread.h>
pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);  初始化读写锁
pthread_rwlock_destroy(pthread_rwlock_t *rwlock);	 销毁读写锁

2. 函数原型:阻塞版本

#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);  读锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);  写锁
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);  解锁

 

三、程序清单

1. 测试代码:

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

int counter;   //全局资源
pthread_rwlock_t rwlock;

void *th_write(void *arg)
{
    int t;
    int i = (int)arg;

    while (1) 
    {
        t = counter;
        usleep(1000);

        pthread_rwlock_wrlock(&rwlock);
        printf("=======write %d: %lu: counter = %d ++counter = %d\n", i, pthread_self(), t, ++counter);
        pthread_rwlock_unlock(&rwlock);

        usleep(5000);
    }
    return NULL;
}

void *th_read(void *arg)
{
    int t;
    int i = (int)arg;

    while (1) 
    {
        pthread_rwlock_rdlock(&rwlock);
        printf("---------------read %d: %lu: %d\n", i, pthread_self(), counter);
        pthread_rwlock_unlock(&rwlock);

        usleep(900);
    }
    return NULL;
}

int main()
{
    int i;
    pthread_t tid[8];
    pthread_rwlock_init(&rwlock, NULL);

    for (i = 0; i < 3; ++i)
        pthread_create(&tid[i], NULL, th_write, (void*)i);
    for (i = 0; i < 5; ++i)
        pthread_create(&tid[i + 3], NULL, th_read, (void*)i);
    for (i = 0; i < 8; ++i)
        pthread_join(tid[i], NULL);
    pthread_rwlock_destroy(&rwlock);
    return 0;
}

输出结果:

 

互斥锁实现读写锁
 

#include<pthread.h>
 
pthread_mutex_t rdLock = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t wrLock = PTHREAD_MUTEX_INITIALIZER;
int readCnt = 0;//设置读锁调用次数的标志位
 
//实现读锁(共享锁)
void rdLock() 
{
    pthread_mutex_lock(&rdLock);
    readCnt++;
    if (readCnt == 1)//有人读,于是阻塞写锁
        pthread_mutex_lock(&wrLock);
    pthread_mutex_unlock(&rdLock);
}
 
void rdUnlock() 
{
    pthread_mutex_lock(&rdLock);
    readCnt--;
    if (readCnt == 0)//表示已没有人在读,释放写锁,可以写入了
        pthread_mutex_unlock(&wrLock);
    pthread_mutex_unlock(&rdLock);
}
 
void wrLock() 
{
    pthread_mutex_lock(&wrLcok);
}
 
void wrUnlock() 
{
    pthread_mutex_unlock(&wrLock);
}

一、实验项目

问题描述】程序 trainticket 中,有 100 个线程,其中 90 个线程是查余票数量的,只有 10 个线程抢票,每个线程一次买 10 张票。初始状态下一共有 1000 张票。因此执行完毕后,还会剩下 900 张票。

程序 trainticket 在运行的时候需要传入参数,即:

  • 参数 0:表示不加任何锁
  • 参数 1:表示使用读写锁
  • 参数 2:表示使用互斥量
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
 
struct Ticket 
{
    int remain;              // 余票数,初始化为 1000
    pthread_rwlock_t rwlock; // 读写锁
    pthread_mutex_t mlock;   // 互斥锁,主要是为了和读写锁进行对比
}ticket;
 
// 通过命令行传参数来取得这个值,用来控制到底使用哪一种锁
// 0:不加锁 1:加读写锁 2:加互斥锁
int lock = 0;
 
void* query(void* arg)  //查票线程
{
    int name = (int)arg;
    sleep(rand() % 5 + 1);
    if (lock == 1)
        pthread_rwlock_rdlock(&ticket.rwlock); // 读模式加锁
    else if (lock == 2)
        pthread_mutex_lock(&ticket.mlock);
 
    int remain = ticket.remain;
    sleep(1);
    printf("%03d query: %d\n", name, remain);
 
    if (lock == 1)
        pthread_rwlock_unlock(&ticket.rwlock);
    else if (lock == 2)
        pthread_mutex_unlock(&ticket.mlock);
 
    return NULL;
}
 
 
void* buy(void* arg)  // 抢票线程
{
    int name = (int)arg;
 
    if (lock == 1)
        pthread_rwlock_wrlock(&ticket.rwlock); // 写模式加锁
    else if (lock == 2)
        pthread_mutex_lock(&ticket.mlock);
 
    int remain = ticket.remain;
    remain -= 10; // 一次买 10 张票
    sleep(1);
    ticket.remain = remain;
    printf("%03d buy 10 tickets\n", name);
 
    if (lock == 1)
        pthread_rwlock_unlock(&ticket.rwlock);
    else if (lock == 2)
        pthread_mutex_unlock(&ticket.mlock);
 
    sleep(rand() % 5 + 2);
    return NULL;
}
 
int main(int argc, char* argv[]) 
{
    lock = 0;
    if (argc >= 2) 
        lock = atoi(argv[1]);
 
    int names[100];
    pthread_t tid[100];
 
    int i;
    for (i = 0; i < 100; ++i) 
        names[i] = i;
    ticket.remain = 1000;
    printf("remain ticket = %d\n", ticket.remain);
 
    pthread_rwlock_init(&ticket.rwlock, NULL);
    pthread_mutex_init(&ticket.mlock, NULL);
 
    for (i = 0; i < 100; ++i) {
        if (i % 10 == 0)
            pthread_create(&tid[i], NULL, buy, (void*)names[i]);
        else
            pthread_create(&tid[i], NULL, query, (void*)names[i]);
    }
 
    for (i = 0; i < 100; ++i) 
        pthread_join(tid[i], NULL);
    pthread_rwlock_destroy(&ticket.rwlock);
    pthread_mutex_destroy(&ticket.mlock);
    printf("remain ticket = %d\n", ticket.remain);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值