理解计算机系统_并发编程(9)_线程(六):读者-写者问题

前言
       

        以<深入理解计算机系统>(以下称“本书”)内容为基础,对程序的整个过程进行梳理。本书内容对整个计算机系统做了系统性导引,每部分内容都是单独的一门课.学习深度根据自己需要来定

引入

        接续理解计算机系统_并发编程(8)_线程(五):生产者-消费者问题-优快云博客,这篇帖子看读者-写者问题.

线程的分析

线程的"不确定性"

        目前对线程的理解有了一些基础,再想想这部分内容给人一种"不确定"的感觉.编程是逻辑的科学,逻辑代表"绝对理性"或者"必然性".学习也在追求一种"必然",学会的东西照着这种"必然"应用在同样的情境下.分析下线程的"不确定性"来源:

        1>线程代码是"乱序"的.由于CPU的中断机制,在访问和修改共享变量时必须考虑同步.虽然通过信号量加锁可以解决这个问题.但读代码的时候必须时刻考虑:某条语句执行完以后从前面某个位置开始..

        2>线程由内核调度,无法追踪.

        假设程序员写了n个线程,要求其中第1个线程具有优先级,例如每次经过P(v)操作都排在第一,因为由内核调度,所以无法实现(如果修改内核可以当笔者没说,但目前掌握的知识是无法实现的).

=============================内容分割线↓===================================

 以第2条延伸思考,如何让某个线程具有优先权?他可以通过线程函数thread实现

免责声明:以下内容纯属虚构(如有雷同实属巧合)

假如有一个想在网上抢票的黄牛(scalper),他该怎样做? 

服务器端的程序员在写thread函数的同时,加入一些内容,当检测到其地址时,给与一些"便利".

---以上是异想天开内容

=============================内容分割线↑===================================

        小结:线程的产生是不可控的,线程的内容是可控的.

线程的抽象

        逻辑代表理性.在理解和记忆某个概念时,建立一种"感性"的抽象.尝试建立起对线程的抽象

                1>共享数据.为了解决同步问题,引入了信号量做互斥锁

                2>共享函数.

                3>不可知的执行顺序

=============================内容分割线↓===================================

         引申:把1和2综合起来,一个线程每次修改数据,调用相同的函数.是不是很像一个for循环?即一个单任务可以化成一个多线程任务.这样做可能是"画蛇添足",也可能十分有用.

        以"有用"做前提,分析他的优点来自于:用计算机(线程)并发提高工作效率.

        再想一想适用于什么样的场景,笔者想了两个

        1>大型单机应用程序的数据读写,比如某个游戏的图像渲染(本书虽然没有提到多核CPU对线程的支持,但那不是写应用程序员该关心的事).单机任务分解成多线程任务

        2>对于服务器而言,这样做能不能提高效率?  这个话题很大---如何提高服务器效率?

        笔者感觉问题的核心在于:每个线程函数(thread函数)的代码量多少才合适?

        简单考虑影响线程代码量多少的因素:服务器CPU的计算能力,用户体验等等.需要写代码验证

         他甚至可以作为程序员职业的主力方向去努力.

=============================内容分割线↑===================================

基于线程抽象产生的程序模型

        笔者把几种程序模型,归纳为基于线程的抽象(个人专利(*^_^*),转载请注明出处)

        1.任务线程化模型.

                变化的共享数据,加共享函数,使单任务多线程解决,提高工作效率.

        2.生产者-消费者模型

                共享函数返回值产生数据集合,形成缓冲区.自动调节生产者和消费者.

        3.读者-写者模型

                共享数据可被多个线程同时访问,但修改时必须只能有一个线程.

=============================内容分割线↓===================================

        /*能思考出程序模型并写代码实现的,被视为行业先驱,多积累多思考,说不定某天自己写出来一个*/

        此外,这几种已实现的程序模型,能不能"组合"起来,应对某种场景?留意

=============================内容分割线↑===================================

读者-写者模式应用场景

        本书P707第2段:读者-写者交互在现实系统中很常见.例如,一个在线航空预定系统中,允许有无限多个客户同时查看座位分配,但是正在预定座位的客户必须拥有对数据库的独占的访问.

        再看另一个例子,在一个多线程缓存Web代理中,无限多个线程可以从共享页面缓存中取出已有的页面,但是任何向缓存中写入一个新页面的线程必须拥有独占的访问. ---黑体字是原话

代码解读

        本书图12-26举了一个"读者优先"的例子

        首先,从非线程函数到线程函数,才开始非常不习惯(目前笔者尚在适应).线程函数是非常难读写的,原因是前面讲过:线程代码是乱序的.

int readcnt;                //读操作计数,初始化为0;
sem_t mutex,w;              //两个锁--读锁和写锁,初始化为1
    
void reader(void)           //读操作 
{
    while(1){
        P(&mutex);          //第7行  
        readcnt++;          //第8行  
        if(readcnt==1)      //第9行    
            P(&w);          //第10行  
        V(&mutex);          //第11行    
        /* 读发生 */
        
        P(&mutex);           //第14行:第2个P操作
        readcnt--;
        if(readcnt==0) 
            V(&w);           //第17行 
        V(&mutex);            
    }

void writer(void)            //写操作
{
     while(1){
        P(&w);
       /* 写发生 */
        V(&w); 
     }

}

        整体是这个意思(需求):允许有n个线程进行读操作,但只能有1个线程进行写操作.分两种情况:

        1.如果当前线程进行读操作,无法进行写操作,因为第10行写锁发挥作用.必须等所有读线程完成后,写锁才被解开(第17行).下一个进来的线程可以进行写操作.这正是体现了"读者优先"

        2.如果当前线程在进行写操作,不影响其他线程进行读操作.

        其他:

        1.从第7行到第11行,用readcnt控制写锁.第11行解开读锁,意思是从读操作来看,7~11行可以省略,任意读.所以,第7到11行其实就干了一件事:掌握写锁.从第14行开始,如果达到条件:所有读的线程执行完毕,才解开写锁.

        2.注意:代码无法应用到生产环境,需要增加内容,没有共享数据(readcnt不算,他用来控制写锁).具体不展开

        3.如何实现 "写者优先"?很简单,把两个函数名调换一下.全局变量中把readcnt改成writecnt

小结

        读者-写者模式简单解读

        线程有很多地方值得思考,代码读写要困难一些.

        并发是程序设计的重点,也是难点,需要多多研究.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重庆彭枫

你的鼓励是我创作的动力,谢谢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值