• 对协程的一点理解


    什么是协程

    A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes.

    也就是说,协程是一个函数,可以被挂起和被恢复。

    协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程那样需要上下文切换来消耗资源,因此协程的开销远远小于线程的开销。

    子程序

    子程序,就是函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。

    协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

    注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断。比如子程序A、B:

    def A():
        print '1'
        print '2'
        print '3'
    
    def B():
        print 'x'
        print 'y'
        print 'z'

    假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:

    1
    2
    x
    y
    3
    z

    但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。

    看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那

    和多线程比,协程有何优势?

    1. 切换开销小,执行效率高

    因为子程序切换不是线程切换,而是由程序自身控制

    2. 不需要多线程的锁机制

    因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了

    子程序与协程的差异:

    协程可以通过yield(取其“让步”之义而非“出产”)来调用其它协程,接下来的每次协程被调用时,从协程上次yield返回的位置接着执行,通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的

    1. 子程序可以调用其他子程序,调用者等待被调用者结束后继续执行,故而子程序的生命期遵循后进先出,即最后一个被调用的子例程最先结束返回。协程的生命期完全由对它们的使用需要来决定。(也就是想怎么跳就怎么跳,不需要遵循函数调用那套规则)

    2. 子程序的起始处是惟一的入口点,而协程可以有多个入口点,协程的起始处是第一个入口点,每个yield返回出口点都是再次被调用执行时的入口点。

    3. 子例程只在结束时一次性的返回全部结果值。协程可以在yield时不调用其他协程,而是每次返回一部分的结果值,这种协程常称为生成器迭代器

    子例程是协程的特里,因为任何子例程都可以看作是不调用yield的协程

    示例

    这里是一个简单的例子证明协程的实用性。

    传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。

    如果改用协程,生产者生产消息后,直接通过yield跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:

    var q := 新建队列
    
    coroutine 生产者
        loop
            while q 不满载
                建立某些新产品
                向 q 增加这些产品 
            yield 给消费者
    
    coroutine 消费者
        loop
            while q 不空载
                从 q 移除某些产品
                使用这些产品
            yield 给生产者

    应用场景

    根据今天查阅的资料来看,协程的应用场景主要在于 :I/O 密集型任务。

    当程序在执行 I/O 时操作时,时间片的算法并不知道,就分配了时间片给他,此时CPU 是空闲的,因此可以充分利用 CPU 的时间片来处理其他任务。
    - 在单线程中,一个函数调用,一般是从函数的第一行代码开始执行,结束于 return 语句、异常或者函数执行(也可以认为是隐式地返回了 None )。(相当于被阻塞了,CPU就被浪费了)
    - 有了协程,我们在函数的执行过程中,如果遇到了耗时的 I/O 操作,函数可以临时让出控制权,让 CPU 执行其他函数,等 I/O 操作执行完毕以后再收回控制权。(相当于暂时跳过去)

    参考链接

  • 相关阅读:
    leetcode-62-Unique Paths
    [leetcode-64-Minimum Path Sum]
    [leetcode-198-House Robber]
    [leetcode-120-Triangle]
    [leetcode-53-Maximum Subarray]
    [leetcode-303-Range Sum Query
    [leetcode-123-Best Time to Buy and Sell Stock III]
    绑定下拉框
    解决URL参数中文乱码
    html 图片拖动不出来的脚本
  • 原文地址:https://www.cnblogs.com/lfri/p/12527766.html
Copyright © 2020-2023  润新知