zoukankan      html  css  js  c++  java
  • 一个故事讲完进程、线程和协程

     

     

     

    很久以前,有两个程序,暂且称他们旺财和小强吧。

    旺财和小强这两个程序都很长,每个都有十几万行。 他们两个的人生价值就是到CPU上去运行,把运行结果告诉人类。

    CPU是稀缺资源,只有一个,他们俩必须排着队,轮流使用。

    旺财从头到尾执行完了,让出CPU, 让小强从头儿去执行。

    人类把这种处理方式叫做批处理。

     

     

     

     

     

     

    进程

    长久以来,两人相安无事。 后来CPU的速度越来越快, 远远超过了内存,硬盘的速度。

    人类想到,这批处理系统的效率有点低啊,你看当小强需要从硬盘上读取数据的时候,CPU也一直在等待,这是多大的浪费啊!这时候完全可以让旺财来运行一下嘛!

    当然得保存好小强的执行现场:具体执行到那一行程序指令了, 函数调用到什么层次了,每个函数调用都有什么样的参数,CPU寄存器中的值..... 等等一系列东西。

    如果不把小强的执行现场给保存下来,等到小强的数据从银盘读完了,就没法回到中断处来继续执行了。

    这个执行现场,再加上小强的代码,就是一个执行中的程序,被称为“进程” 。

    旺财和小强在运行的时候,也被改造成了进程。

    人类还规定:进程不能长时间占据CPU, 只能在CPU上执行一小会儿,然后马上切换到别的进程去执行。

    旺财和小强不以为意:不就是执行一会儿,歇一会儿,然后继续执行嘛!

     

     

     

    但是他们不知道的是,由于CPU运行速度超快,旺财和小强虽然在不断地切换运行,在人类那缓慢的世界里看来,旺财和小强好像是同时在执行一样。 这就是并发。

     

     

     

     

     

     

    (在人类看来,小强和旺财似乎是在同时执行)

    多年以后,他们俩才真正地实现了并行: 在一个豪华电脑中,每人都被分配了一个CPU , 真正地同时执行, 这是后话了。

    线程

    这时候旺财已经有了界面,还能访问网络,每当它联网的时候(这也是个非常非常耗时的操作),就得把CPU让给小强。

    即使旺财再次被调度执行,由于网络数据还没有返回,他必须等待,什么事情都做不了,在人类看来,界面根本无法操作,旺财不响应了! 气得人类经常把旺财kill掉。

    旺财心里苦,他很纳闷小强怎么就没有问题,小强不是要读写硬盘吗? 那也是很慢的操作啊。

    小强说:“你傻啊,内部只有一个执行的流程,一遇到耗时的操作就得等待,你看看我,内部搞了两个执行流程(线程),一个用来读写硬盘(T1),另外一个处理界面(T2)。我和操作系统商量好了,如果T1在读写硬盘, 就可以调度我的T2来执行,这样界面至少还可以操作。 ”

     

     

     

     

     

     

    旺财觉得很有意思,也采用了类似办法。

    于是,一个进程中至少有一个执行的流程(主线程),也可以开启新的执行流程(线程)。

    线程变成了最小的调度单位。

    协程

    这一天,旺财被一个叫做生产者和消费者的问题折腾地死去活来,两个线程,一个线程向队列中放数据,另外一个从队列中取数据,处理起两个线程的协作就显得很麻烦,不但需要加锁,还得做好线程的通知和等待。

    正在感慨多线程编程之难的时候, 旺财震惊地发现,小强用了一个极为简单的办法把生产者,消费者问题给解决了。

    这个方法的代码如下:

    # 生产者

    def producer(c):

    #其他代码

    while True:

    value = ...生成数据...

    c.send(value)

    # 消费者

    def consumer():

    #其他代码

    while True:

    value = yield

    print(value)

    c = consumer()

    producer(c)

    “这....这怎么执行啊,那个yield是怎么回事?” 旺财表示不解。

    “简单啊,你看那个生产者,是不是向消费者发送了数据? ” 小强说。

    “对啊,然后呢,生产者发送了数据以后,会马上进行下一轮循环吗?”

    “这就是关键所在了,”小强说,“ 它们是这么执行的:”

    生产者发送数据,暂停运行,不进行下一轮循环

    消费者其实一直在value = yield 那里等待,直到数据到来,现在数据来了,取出处理(value就是生产者发送过来的数据)。

    消费者在循环中再次yield, 暂停执行。

    生产者继续下一轮的循环,生成新的消息,发送给消费者。

     

     

     

     

     

     

    旺财觉得很吃惊,小强竟然可以让一个正在执行的程序暂停,他不由得问道:“你这个暂停是真的停止了了,还是说只是像Java的yield那样,让出CPU进入了就绪状态? 等待下次调度运行?”

    “是真的暂停了,程序就停在那里,等待运行控制权从对方那里转移过来。”

    “这不是操作系统干的事情吗? ” 旺财更加吃惊了。

    “正是这样,” 小强得意地说:“我打算把类似生产者,消费者这样的代码称为‘协程’, 这个协程有个重要的特点,就是完全被我所调度和掌控, 不用操作系统介入。”

    “这个协程和线程似乎很像啊。每次协程停止执行的时候,也得保存现场,要不然没法恢复执行。” 旺财说。

    “是啊,只是他们比线程更加轻量级,操作系统内核不用参与,相当于用户态线程了,协程的开销极小,可以轻松地创建大量的协程来做事情。 对了,也许你注意到了,我这两个协程是'合作式'的,它们两个同一时刻只能有一个在运行。 实际上,我在底层可以用一个线程去执行这两个协程。 ”

    旺财表示同意:“不错,既然两个程序可以'合作',那就不用加锁了,也不用在代码里写什么wait和notify了,在程序层面,可以用同步的方式实现异步的功能了! 代码很清晰,我也搞个协程来玩玩吧!”

    (完)

    原文链接:https://www.toutiao.com/i6580123354085196302/

  • 相关阅读:
    luogu P1833 樱花 看成混合背包
    luogu P1077 摆花 基础记数dp
    luogu P1095 守望者的逃离 经典dp
    Even Subset Sum Problem CodeForces
    Maximum White Subtree CodeForces
    Sleeping Schedule CodeForces
    Bombs CodeForces
    病毒侵袭持续中 HDU
    病毒侵袭 HDU
    Educational Codeforces Round 35 (Rated for Div. 2)
  • 原文地址:https://www.cnblogs.com/pipci/p/10174692.html
Copyright © 2011-2022 走看看