关于python:程序员应如何理解协程

44次阅读

共计 3184 个字符,预计需要花费 8 分钟才能阅读完成。

作为程序员,想必你多多少少听过 协程 这个词,这项技术近年来越来越多的呈现在程序员的视线当中,尤其高性能高并发畛域。当你的同学、共事提到协程时 如果你的大脑一片空白,对其毫无概念。。。

那么这篇文章正是为你量身打造的。

话不多说,明天的主题就是作为程序员,你应该如何彻底了解协程。

一般的函数

咱们先来看一个一般的函数,这个函数非常简单:

def func():
print(“a”)
print(“b”)
print(“c”)

这是一个简略的一般函数,当咱们调用这个函数时会产生什么?

  1. 调用 func
  2. func 开始执行,直到 return
  3. func 执行实现,返回函数 A

是不是很简略,函数 func 执行直到返回,并打印出:

a
b
c

So easy,有没有,有没有!

很好!

留神这段代码是用 python 写的,但本篇对于协程的探讨实用于任何一门语言,咱们只不过恰好应用了 python 来用作示例,因为其足够简略。

那么协程是什么呢?

从一般函数到协程

接下来,咱们就要从一般函数过渡到协程了。

和一般函数只有一个返回点不同,协程能够有 多个返回点

这是什么意思呢?

void func() {
print(“a”)
暂停并返回
print(“b”)
暂停并返回
print(“c”)
}

一般函数下,只有当执行完 print(“c”)这句话后函数才会返回,然而在协程下当执行完 print(“a”)后 func 就会因“暂停并返回”这段代码返回到调用函数。

有的同学可能会一脸懵逼,这有什么神奇的吗?我写一个 return 也能返回,就像这样:

void func() {
print(“a”)
return
print(“b”)
暂停并返回
print(“c”)
}

间接写一个 return 语句的确也能返回,但这样写的话 return 前面的代码都不会被执行到了

协程之所以神奇就神奇在当咱们从协程返回后 还能持续调用该协程 ,并且是 从该协程的上一个返回点后继续执行

这足够神奇吧,就好比孙悟空说一声“定”,函数就被暂停了:

void func() {
print(“a”)

print(“b”)

print(“c”)
}

这时咱们就能够返回到调用函数,当调用函数什么时候想起该协程后能够再次调用该协程,该协程会从上一个返回点继续执行。

Amazing,有没有,有没有!

十分好!

只不过孙大圣应用的口诀“定”字,在编程语言中个别叫做 yield(其它语言中可能会有不同的实现,但实质都是一样的)。

须要留神的是,当一般函数返回后,过程的地址空间中不会再保留该函数运行时的任何信息,而协程返回后,函数的运行时信息是须要保留下来的,那么函数的运行时状态到底在内存中是什么样子呢,对于这个问题你能够参考_这里_。

接下来,咱们就用理论的代码看一看协程。

show me the code

上面咱们应用一个实在的例子来解说,语言采纳 python,不相熟的同学不必放心,这里不会有了解上的门槛。

在 python 语言中,这个“定”字同样应用关键词 yield,这样咱们的 func 函数就变成了:

void func() {
print(“a”)
yield
print(“b”)
yield
print(“c”)
}

留神,这时咱们的 func 就不再是简简单单的函数了,而是降级成为了协程,那么咱们该怎么应用呢,很简略:

1 def A():
2   co = func() # 失去该协程
3   next(co)   # 调用协程
4   print(“in function A”) # do something
5   next(co)   # 再次调用该协程

咱们看到尽管 func 函数没有 return 语句,也就是说尽管没有返回任何值,然而咱们仍然能够写 co = func()这样的代码,意思是说 co 就是咱们拿到的协程了。

接下来咱们调用该协程,应用 next(co),运行函数 A 看看执行到第 3 行的后果是什么:

a

显然,和咱们的预期一样,协程 func 在 print(“a”)后因执行 yield 而暂停并返回函数 A。

接下来是第 4 行,这个毫无疑问,A 函数在做一些本人的事件,因而会打印:

a
in functino A

接下来是重点的一行,当执行第 5 行再次调用协程时该打印什么呢?

如果 func 是一般函数,那么会执行 func 的第一行代码,也就是打印 a。

但 func 不是一般函数,而是协程,咱们之前说过,协程会在上一个返回点持续运行,因而这里应该执行的是 func 函数第一个 yield 之后的代码,也就是 print(“b”)。

a
in functino A
b

看到了吧,协程是一个很神奇的函数,它会本人记住之前的执行状态,当再次调用时会从上一次的返回点继续执行。

神奇不神奇,厉害不厉害!

Very Good.

图形化解释

为了让你更加彻底的了解协程,咱们应用图形化的形式再看一遍,首先是一般的函数调用:

在该图中,方框内示意该函数的指令序列,如果该函数不调用任何其它函数,那么应该从上到下顺次执行,但函数中能够调用其它函数,因而其执行并不是简略的从上到下,箭头线示意执行流的方向。

从图中咱们能够看到,咱们首先来到 funcA 函数,执行一段时间后发现调用了另一个函数 funcB,这时管制转移到该函数,执行实现后回到 main 函数的调用点继续执行。

这是一般的函数调用。

接下来是协程。

在这里,咱们仍然首先在 funcA 函数中执行,运行一段时间后调用协程,协程开始执行,直到第一个挂终点,尔后就像一般函数一样返回 funcA 函数,funcA 函数执行一些代码后再次调用该协程,留神,协程这时就和一般函数不一样了,协程并不是从第一条指令开始执行而是 从上一次的挂终点开始执行,执行一段时间后遇到第二个挂终点,这时协程再次像一般函数一样返回 funcA 函数,funcA 函数执行一段时间后整个程序完结。

函数只是协程的一种特例

怎么样,神奇不神奇,和一般函数不同的是,协程能晓得本人上一次执行到了哪里

当初你应该明确了吧,协程会在函数被暂停运行时保留函数的运行状态,并能够从保留的状态中复原并持续运行。

很相熟的滋味有没有,这不就是操作系统对线程的调度嘛,线程也能够被暂停,操作系统保留线程运行状态而后去调度其它线程,尔后该线程再次被调配 CPU 时还能够持续运行,就像没有被暂停过一样。

只不过线程的调度是操作系统实现的,这些对程序员都不可见,而协程是在用户态实现的,对程序员可见。

这就是为什么有的人说能够把协程了解为用户态线程的起因。

此处应该有掌声。

也就是说当初程序员能够表演操作系统的角色了,你能够本人管制协程在什么时候运行,什么时候暂停,也就是说协程的调度权在你本人手上。

在协程这件事儿上,调度你说了算

当你在协程中写下 yield 的时候就是想要暂停改协程,当应用 next()时就是要再次运行该协程。

当初你应该了解为什么说函数只是协程的一种特例了吧,函数其实只是没有挂终点的协程而已。

协程的历史

有的同学可能认为协程是一种比拟新的技术,然而其实协程这种概念早在 1958 就曾经提出来了,要晓得这时线程的概念都还没有提出来

到了 1972 年,终于有编程语言实现了这个概念,这两门编程语言就是 Simula 67 以及 Scheme。

但协程这个概念始终没有流行起来,甚至在 1993 年还有人考古一样专门写论文挖出协程这种古老的技术。

因为这一时期还没有线程,如果你想在操作系统写出并发程序那么你将不得不应用相似协程这样的技术,起初线程开始呈现,操作系统终于开始原生反对程序的并发执行,就这样,协程逐步淡出了程序员的眼帘。

直到近些年,随着互联网的倒退,尤其是挪动互联网时代的到来,服务端对高并发的要求越来越高,协程再一次重回技术支流,各大编程语言都曾经反对或打算开始反对协程。

当初你应该对协程有一个清晰的认知了吧。

总结

到这里你应该曾经了解协程到底是怎么一回事,然而,仍然有一个问题没有解决,为什么协程这种技术又一次重回眼帘,协程实用于什么场景下呢?该怎么应用呢?

对于这些问题,下一篇文章将会给你答案。

心愿这篇对你了解协程有所帮忙。

正文完
 0