协程学习笔记

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了协程学习笔记相关的知识,希望对你有一定的参考价值。

一、协程简介

什么是协程?

协程,又称微线程,线程,英文名Coroutine。协程是一种用户态的轻量级线程

协程拥有自己的寄存器上下文和栈。

简单来说,协程就是来回切换,当遇到IO操作,如读写文件,网络操作时,就跳到另一个线程执行,再遇到IO操作,又跳回来。不断的跳过去跳过来执行,因为速度很快,所以看起来就像是执行的并发,实质上是单线程。

协程的好处:

  1. 无需线程上下文切换的开销
  2. 无需原子操作锁定及同步的开销
  3. 方便切换控制流,简化编程模型
  4. 高并发+高扩展性+低成本
  5. 一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理

协程的缺点:

  1.无法利用多核资源

协程的本质是个单线程,不能同时将单个CPU 的多个核用上。
协程需要和进程配合才能运行在多CPU上,日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用

2.进行阻塞(Blocking)操作(如IO时,读写文件,网络)会阻塞掉整个程序

二、yield实现最简单协程效果

在单线程中实现并发效果

以简单的生产者消费者模型为例,生产者厨师做包子,消费者吃包子

代码1:

  说明:1个生产者,每次做1个包子,只做5个,2个消费者

 1 import time
 2 import queue
 3 
 4 def consumer(name):
 5     print("%s准备开始吃包子啦..." % name)
 6     while True:
 7        new_baozi = yield
 8         print("[%s] 正在吃包子 %s" % (name,new_baozi))
 9         #time.sleep(1)
10 
11 def producer():
12     r = con.__next__()
13     r = con2.__next__()
14    n = 0
15     while n < 5:    # 生产者只做5个包子
16         n += 1
17         print("\033[32;1m[producer]\033[0m做好了新包子 %s" % n )
18        time.sleep(1)
19        con.send(n)
20        con2.send(n)
21 
22 if __name__ == __main__:
23     # 定义2个消费者、1个生产者
24     con = consumer("c1")
25    con2 = consumer("c2")
26    p = producer()
27 
28 # 显示结果:
29 # c1准备开始吃包子啦...
30 # c2准备开始吃包子啦...
31 # [producer]做好了新包子 1
32 # [c1] 正在吃包子 1
33 # [c2] 正在吃包子 1
34 # [producer]做好了新包子 2
35 # [c1] 正在吃包子 2
36 # [c2] 正在吃包子 2
37 # [producer]做好了新包子 3
38 # [c1] 正在吃包子 3
39 # [c2] 正在吃包子 3
40 # [producer]做好了新包子 4
41 # [c1] 正在吃包子 4
42 # [c2] 正在吃包子 4
43 # [producer]做好了新包子 5
44 # [c1] 正在吃包子 5
45 # [c2] 正在吃包子 5

代码2:

  说明:生产者只做5次包子,每次2个,2个消费者

 1 import time
 2 import queue
 3 
 4 def consumer(name):
 5     print("%s准备开始吃包子啦..." % name)
 6     while True:
 7         new_baozi = yield
 8         print("[%s] 正在吃包子 %s" % (name,new_baozi))
 9         #time.sleep(1)
10 
11 def producer(name):
12     r = con.__next__()
13     r = con2.__next__()
14     n = 0
15     m = 0
16     count = 0
17     while count < 5:    # 生产者只做5次包子,每次2个
18         n = m + 1
19         m = n + 1
20         count += 1
21         print("\033[32;1m厨师%s\033[0m做好了新包子 %s" % (name,n) )
22         print("\033[32;1m厨师%s\033[0m做好了新包子 %s" % (name,m) )
23         time.sleep(1)
24         con.send(n)
25         con2.send(m)
26 
27 if __name__ == __main__:
28     # 定义2个消费者、1个生产者
29     con = consumer("莉莉")
30     con2 = consumer("静静")
31     p = producer("小白")
32 
33 # 显示结果:
34 # 莉莉准备开始吃包子啦...
35 # 静静准备开始吃包子啦...
36 # 厨师小白做好了新包子 1
37 # 厨师小白做好了新包子 2
38 # [莉莉] 正在吃包子 1
39 # [静静] 正在吃包子 2
40 # 厨师小白做好了新包子 3
41 # 厨师小白做好了新包子 4
42 # [莉莉] 正在吃包子 3
43 # [静静] 正在吃包子 4
44 # 厨师小白做好了新包子 5
45 # 厨师小白做好了新包子 6
46 # [莉莉] 正在吃包子 5
47 # [静静] 正在吃包子 6
48 # 厨师小白做好了新包子 7
49 # 厨师小白做好了新包子 8
50 # [莉莉] 正在吃包子 7
51 # [静静] 正在吃包子 8
52 # 厨师小白做好了新包子 9
53 # 厨师小白做好了新包子 10
54 # [莉莉] 正在吃包子 9
55 # [静静] 正在吃包子 10

 

用sleep,模拟IO操作阻塞
一遇到阻塞,怎么把阻塞丢给操作系统,yield实现不了,用协程可以实现

三、greenlet模块

greenlet,协程模块,是一个第三方模块,不是Python自带的,需要安装才能够使用。
导入模块命令:from greenlet import greenlet

代码示例:

技术分享
 1 from greenlet import greenlet
 2 
 3 def test1():
 4     print(12)
 5    gr2.switch()
 6     print(34)
 7    gr2.switch()
 8 
 9 def test2():
10     print(56)
11    gr1.switch()
12     print(78)
13 
14 gr1 = greenlet(test1)
15 gr2 = greenlet(test2)
16 gr1.switch()
17 
18 # 显示结果:
19 # 12
20 # 56
21 # 34
22 # 78
23 
24 # 说明:先执行test1函数,打印了12,切换到test2函数,打印出56,再返回到test1(先前切换的地方),打印34,又切换到test,打印78
25 # 协程,就是来回的切换
greenlet模块协程示例

 

先写到这里...

 

以上是关于协程学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

Python 学习笔记 - 协程

Python协程学习笔记

python 3.x 学习笔记17(协程以及I/O模式)

2023爬虫学习笔记 -- 协程操作

python学习笔记-(十四)进程&协程

2023爬虫学习笔记 -- 通过协程下载m3u8视频