python学习--协程

这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

什么是协程

协程,其实可以理解为一种特殊的程序调用。特殊的是在执行过程中,在子程序(或者说函数)内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

注意,它有两个特征:

可中断,这里的中断不是普通的函数调用,而是类似CPU的中断,CPU在这里直接释放转到其他程序断点继续执行。

可恢复,等到合适的时候,可以恢复到中断的地方继续执行,至于什么是合适的时候,我们后面再探讨。

和进程线程的区别

上面两个特点就导致了它相对于线程和进程切换来说极高的执行效率,为什么这么说呢?我们先老生常谈地说一下进程和线程。

进程是操作系统资源分配的基本单位,线程是操作系统调度和执行的最小单位

  • 进程是程序的启动实例,拥有代码和打开的文件资源、数据资源、独立的内存空间。
  • 线程从属于进程,是程序的实际执行者,一个进程至少包含一个主线程,也可以有更多的子线程,线程拥有自己的栈空间。
  • 无论是进程还是线程,都是由操作系统所管理和切换的。

协程

它又叫做微线程,但其实它和进程还有线程完全不是一个维度上的概念

  • 进程和线程的切换完全是用户无感,由操作系统控制,从用户态到内核态再到用户态
  • 协程的切换完全是程序代码控制的,在用户态的切换,就像函数回调的消耗一样,在线程的栈内即可完成。

python的协程(coroutine)

python的协程其实是我们通常意义上的协程Goroutine

  • python的协程同样是在适当的时候可中断可恢复。
  • 那么什么是适当的时候呢,就是你认为适当的时候,因为程序在哪里发生协程切换完全控制在开发者手里。
  • 对于python来说,由于GIL锁,在CPU密集的代码上做协程切换是没啥意义的,CPU本来就在忙着没偷懒,切换到其他协程,也只是在单核内换个地方忙而已。
  • 我们应该在IO密集的地方来起协程,这样可以让CPU不再空等转而去别的地方干活,才能真正发挥协程的威力。
  • 还可以将协程理解为生成器+调度策略,生成器中的yield关键字,就可以让生成器函数发生中断,而调度策略,可以驱动着协程的执行和恢复。这样就实现了协程的概念。
  • 这里的调度策略可能有很多种,简单的例如忙轮循:while True,更简单的甚至是一个for循环。就可以驱动生成器的运行,因为生成器本身也是可迭代的。

我们看一个简单的协程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ini复制代码import time


def consumer():
    res = ''

    while True:
        n = yield res
        if not n:
            return

        print('[CONSUMER] Consuming %s...' % n)
        time.sleep(1)
        res = '200 OK'

def produce(c):
    c.next()
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        res = c.send(n)
        print('[PRODUCER] Consumer return: %s' % res)
    c.close()


if __name__=='__main__':
    c = consumer()
    produce(c)

很明显这是一个传统的生产者-消费者模型,这里consumer函数就是一个协程(生成器),它在n = yield r 的地方发生中断,生产者produce中的c.send(n),可以驱动协程的恢复,并且向协程函数传递数据n,接收返回结果r。而while n < 5,就是我们所说的调度策略。在生产中,这种模式很适合我们来做一些pipeline数据的消费,我们不需要写死几个生产者进程几个消费者进程,而是用这种协程的方式,来实现CPU动态地分配调度。

python协程的特点

  • 单线程内切换,适用于IO密集型程序中,可以最大化IO多路复用的效果。
  • 无法利用多核。
  • 协程间完全同步,不会并行。不需要考虑数据安全。
  • 用法多样,可以用在web服务中,也可用在pipeline数据/任务消费中

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%