• 读写锁的一个奇怪表现


    上周做性能调优的时候,发现一个测并发读写的场景数据很奇怪。

    场景是测一个写线程加不同数量的读线程时的读写QPS,结果发现数据大致是下面的样子:

    写线程数    读线程数    写QPS    读QPS
    1          1          4000     40
    1          5          3000     10000
    1          10         3000     20000
    ...
    

    代码大致是这样子的:

    // 写线程
        ReadWriteLockGuard lock(mLock, 'w');
        // do something...
    
    // 读线程
        ReadWriteLockGuard lock(mLock, 'r');
        // do something...
    

    从这段代码看来,当读写线程是1:1时,应该是两个线程轮流抢锁才对,但QPS却显示出写线程抢到锁的次数是读线程的100倍!

    于是我在读写线程的代码中都加一行打印,来看读写线程抢锁的情况:

    // 写线程
        ReadWriteLockGuard lock(mLock, 'w');
        // do something...
        cout << "w" << endl;
    
    // 读线程
        ReadWriteLockGuard lock(mLock, 'r');
        // do something...
        cout << "r" << endl;
    

    结果很出乎我的意料:

    r
    w
    w
    w
    ...
    w
    r
    w
    w
    ...
    

    总之写线程连续抢到若干次锁后,可怜的读线程才抢到一次锁。

    很奇怪的现象。

    我之前对读写锁抢锁流程的理解:

    1. 如果当前没有线程持有锁,那么第一个去抢锁的活动线程会拿到锁;
    2. 如果当前持有者释放锁,那么所有排队的线程会进行抢锁;
    3. 排队的线程中有等写锁的线程时,申请读锁会阻塞(写锁优先)。

    现在看来这个理解是有问题的,无法解释这一现象。

    和同事交流了一下,读写锁抢锁流程可能是这样的:

    1. 如果有线程申请锁阻塞,会首先调用SpinLock一会,之后如果还是没抢到锁,那么内核将其设置为睡眠状态,并加入等待队列;
    2. 当前持有线程释放锁后,内核将所有等待队列中的睡眠线程唤醒,加入调度队列;
    3. 进入调度队列的竞争线程在被调度运行后,开始抢锁。

    从这个流程来看,我遇到的这种情况可以这么解释:

    1. 读线程首先运行,抢到锁;
    2. 因为是写优先,在读线程结束后锁肯定会让给写线程;
    3. 写线程释放锁后,读线程被唤醒,此时还处于等待状态,未运行,不能抢锁;
    4. 写线程没有睡眠,重新抢锁,此时没有写优先的影响,成功抢到锁;
    5. 读线程开始运行,抢锁失败,重新睡眠。

    在读线程增多以后,写线程释放锁后就不一定能抢到锁了,因此会有一定的时间在睡眠,这样进一步增大了读线程抢到锁的概率,因此就观察到读的QPS猛涨的情况。

    上面的猜测还未得到验证,有空还是得看看pthread_read_write_lock的实现啊。

  • 相关阅读:
    转 meta标签之详解
    面向过程分析方法和面向对象分析方法区别到底在哪里
    几大开发模型区别与联系
    第6周作业
    第5次作业
    第四次作业
    4.回合制战斗游戏中需要哪些基本的元素或者属性来达到战斗乐趣?
    需求获取常见的方法是进行客户访谈,结合你的实践谈谈会遇到什么问题,你是怎么解决的?
    4.你认为一些军事方面的软件系统采用什么样的开发模型比较合适?
    作业三
  • 原文地址:https://www.cnblogs.com/fuzhe1989/p/3876492.html
Copyright © 2020-2023  润新知