02Linux下C语言锁的学习之Linux下的读写锁

02Linux下C语言锁的学习之Linux下的读写锁

概述:
下面的锁的意思均是代表读写锁。
读写锁的特性:

  • 1)若一把锁被一个线程以读方式锁住,当其它线程以读方式上锁的话,那么可以上锁成功。

  • 2)若一把锁被一个线程以写方式锁住,当其它线程以读或者写方式上锁的话,都会被阻塞。

  • 要注意的是:多个线程同时请求锁时,请求读方式的线程会被放在请求写方式的线程后面;若不是同时请求上锁,读方式先请求的则先等已经上锁的线程处理后再处理(当然如果该线程以读方式上锁可以同时进行),即使后面再有写请求也要等其完成才能上锁。 这里的同时和不是同时很重要。

  • 实际上上面的意思可以总结为:

读时共享,写时阻塞(独占),写锁优先级高,注意同时与非同时字眼,注意目前以何种方式上锁。//记住这五句话就可以完全吃透读写锁的各个线程的执行顺序了。具体可以看下面的场景分析。

有点类似fork创建子进程时继承父进程的全局变量。

1 与Linux下的读写锁相关的函数介绍
1)pthread_rwlock_init函数

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
/*
	功能:初始化一把锁。
	参1:读写锁。
	参2:读写锁属性,通常使用默认属性,传NULL即可。
*/

2)pthread_rwlock_destroy函数

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
/*
	功能:销毁一把锁。
	参1:读写锁。
*/

3)pthread_rwlock_rdlock函数

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
/*	
	功能:以只读方式锁住读写锁。
	参1:读写锁。
*/

4)pthread_rwlock_wrlock函数

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
/*
	功能:以读写方式上锁,简称叫以写方式上锁。
	参1:读写锁。
*/

5)pthread_rwlock_unlock函数

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
/*
	功能:解锁。
	参1:读写锁。
*/

6)pthread_rwlock_tryrdlock函数

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
/*
	功能:以读方式尝试请求读写锁(非阻塞请求读锁)。
	参1:读写锁。
*/

7)pthread_rwlock_trywrlock函数

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
/*
	功能:以写方式尝试请求读写锁(非阻塞请求写锁)
	参1:读写锁。
*/

2 读写锁的场景分析
1)场景一:均是读请求。
当下面4个线程请求读写锁时,不管是否是同时,都能成功上锁访问数据。
在这里插入图片描述

2)场景2:T1,T2线程同时上锁,T3,T4在后面请求。
由于T1,T2是同时请求上锁,但写请求优先级高,所以T2先上锁,其它线程阻塞。T2线程处理完后T1,T3,T4均能上锁,上锁的顺序看CPU先处理哪个请求,不需要考虑顺序。
在这里插入图片描述

3)场景3:T1,T2在以读方式上锁时,T3,T4以写方式请求。
因为T1,T2已经上锁,所以T3,T4会阻塞等待,即使你是以写方式请求。
写请求优先级高是针对于没有上锁且是同时请求的情况下。

  • 注意:当一把锁被写方式锁住时,读方式请求先到,然后再以写方式请求,由于锁被锁住,所以这种也认为是同时,写方式请求将被放在读方式请求之前。
    当一把锁被读方式锁住时不需要考虑,因为再以读方式先请求的话会共享,写方式再请求因读方式请求已经共享,所以实际剩下自己,已经无对比可言。
    在这里插入图片描述

4)场景4:T1先以读方式加锁成功,后面三个线程同时请求。
这里再提醒一下:若T1先加锁成功,在操作期间,T2,T3,T4即使按不同顺序(除了T2放在前并且CPU开始处理该请求,否则即使T2先请求但是还在队列时T3请求来了那么顺序发生改变,T2即使在最前同样不可以共享),也是认为是同时(看场景3的解释),因为T1还在操作期间嘛。

当T1在操作期间,由于是同时请求并且写优先级高,那么T3一定会被排在T2,T4线程之后,所以T3不可能共享,必须等到T2,T4处理完后才能访问。
在这里插入图片描述

3 读写锁代码例子
代码很简单,创建了3个写线程,5个读线程。共享资源为int型全局变量。


/* 3个线程不定时 "写" 全局资源,5个线程不定时 "读" 同一全局资源 */

#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) {
		//写线程期间,独占
        pthread_rwlock_wrlock(&rwlock);
		t = counter;
        usleep(1000);//模拟业务,让读线程获取CPU,但是读线程因为锁被锁住而阻塞
        printf("=======write %d: %lu: counter=%d ++counter=%d\n", i, pthread_self(), t, ++counter);
        pthread_rwlock_unlock(&rwlock);
		
        usleep(5000);//让其它线程获取锁,防止本线程回到while又重新上锁,导致其它线程很少机会获取到锁
    }
	
    return NULL;
}

//读线程的回调函数
void *th_read(void *arg)
{
	//区别线程几的因子
    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);//让其它线程获取锁,防止本线程回到while又重新上锁,导致其它线程很少机会获取到锁
    }
	
    return NULL;
}

//这里为了简单没做错误分析
int main(void)
{
    int i;
    pthread_t tid[8];//线程id

    pthread_rwlock_init(&rwlock, NULL);
	
	//创建3个写线程
    for (i = 0; i < 3; i++)
        pthread_create(&tid[i], NULL, th_write, (void *)i);
	
	//创建5个读线程
    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;
}

上面程序会不断打印count被写和读共享时的值。
截图部分分析结果:
1)下图看到,问:write 0:4150012746那行在写期间,黑色部分的上锁请求可能同时进行吗?
答:不可能,如果同时请求,那么根据写请求优先,write 2那行必定在前面的read前面,所以write 2是在那10个读线程之后请求的。
在这里插入图片描述

2)问:蓝色框里面的请求有可能是一起来的吗?
答:就是一起同时来的,然后根据优先级写请求放在前面。因为根据write 2上面是读可以知道,如果不是一起来的话,write 1下面的读请求先于write 2先来,那么是可以共享的,应该在write 2前共享。
在这里插入图片描述

4 总结读写锁
由于读写锁在实际应用很少用,并且麻烦,所以不建议大家使用,并且一般互斥锁就够Linux下的C程序员使用了。这里只是让大家有这个概念。要搞明白读写锁如何排序线程的请求,记住概述的那五句话即可,都是按照那五句话进行排序请求。

读写锁完结。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值