Python并发实践_02_协程

python中实现并发的方式有很多种,通过多进程并发可以真正利用多核资源,而多线程并发则实现了进程内资源的共享,然而Python中由于GIL的存在,多线程是没有办法真正实现多核资源的。

对于计算密集型程序,应该使用多进程并发充分利用多核资源,而在IO密集型程序中,多核优势并不明显,甚至由于大多数时间都是在IO堵塞状态,多进程的切换消耗反而让程序效率更加低下。

而当需要并发处理IO密集型任务时,就需要用到协程(Coroutine)。协程并没有系统级的调度,而是用户级的调度方式,避免了系统调用的开销,虽然协程最终是串行工作,但是却可以实现非常大的并发量。

参考文章:

https://blog.tonyseek.com/post/event-manage-with-greenlet/

producer-consumer

利用yield生成器,可以简单展现协程的工作方式:

import time
def consumer():
    print "Ready to receive"
    while True:
        y = (yield )
        time.sleep(1)
        print "Receive %s from producer”%y
def producer():
    c = consumer()
    c.next()
    i = 1
    while i > 0 and i < 11:
        time.sleep(1)
        print "Send %s to consumer"%i
        c.send(i)
        i += 1
if __name__ == ‘__main__‘:
    producer()

上述过程展示了基本的生产者-消费者模型,消费者consumer是一个生成器;

当第一次在producer中调用c.next()时,激活consumer,并且运行到yield时协程(consumer)被挂起,等待生成器被调用next或者send。

producer进行后续操作,并进入一个循环,每次暂停1s后,向生成器send一个消息,消费者yield获取到该消息,并进行后续的工作。

可以看到,每次yield都需要等待send传入的消息之后才会继续执行之后的任务。

通过yield实现协程

现在要来用yield真正创建一个协程了。

可以想象这样一个模型,一个工地里有很多相似的任务(jobs),并且会源源不断产生这些任务,工地里有一个工头(foreman)负责,工头为了分配任务给工人(worker),会制定一套流程(pipeline)来方便管理:分配工人,验收工作(accept),由于工人工作(work)的时间远远大于分配任务的时间,将这些工人的工作(简单枯燥的重复劳动)看成IO操作的话,这就是一个IO密集型的任务。下面看看python是如何通过yield来实现协程完成真个工作的:

 1 def main():
 2     foreman(args_of_overall,worker_num)
 3
 4 def foreman(args_of_overall,worker_num):
 5     pipeline = create_pipeline(args_of_pipeline,worker_num)
 6     for i,job in enumerate(get_jobs(args_of_ceate_jobs)):
 7         worker_id  = i % worker_num
 8         pipeline.send((job,worker_id))
 9
10 @coroutine
11 def worker(pipeline,accepting,job,my_id):
12     while True:
13         args_of_job, worker_id = (yield )
14         if worker_id == my_id:
15             result = work(args_of_job)
16             accepting.send(result)
17         elif pipeline is not None:
18             pipeline.send((job,worker_id))
19
20 @coroutine
21 def accept():
22     while True:
23         result = (yield )
24         #do_some_accepting
25
26 def create_pipeline(args_of_pipeline,worker_num):
27     pipeline = None
28     accepting = accept()
29     for work_id in range(work_num):
30         pipeline = worker(pipeline,accepting,job,work_id)
31     return pipeline
32
33 def get_jobs(args_of_ceate_jobs):
34     for job in job_source:
35         yield job
36
37 def coroutine(func):
38     def warper(*args):
39         f = func(*args)
40         f.next()
41         return f
42     return warper
43
44 def work(args_of_job):
45     pass
46     #do_some_work
47
48 if __name__ == ‘__main__‘:
49     main()

上述过程中,工人和验收工作都是协程,而get_jobs()函数是一个生成器,当job是动态添加时,就可以改写成一个协程,由此可见,生成器应该是一个特殊的协程。

上述所有的工作都是串行完成,虽然有很多工人,工人之间的工作是并发的(IO等待时间),但是工作一直是从第一个开始一个一个分配任务。

时间: 2024-10-13 12:01:34

Python并发实践_02_协程的相关文章

python并发编程之---协程

1.什么是协程 协程:是单线程下的并发,又称微线程,纤程. 协程是一种用户态的轻量级线程,协程是由用户程序自己控制调度的. 2.需要注意的点: 需要强调的是: #1. python的线程属于内核级别的,即由操作系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其他线程运行) #2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操作系统)控制切换,以此来提升效率(!!!非io操作的切换与效率无关) 对比操作系统控制线程的切换,用户在单线程内控制协程的切换 优点

python并发编程之协程

阅读目录 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作系统强制控制),一种情况是该任务发生了阻塞,另外一种情况是该任务计算的时间过长 ps:在介绍进程理论时,提

Python并发编程:协程介绍

一 引子 基于单线程来实现并发,即只用一个主线程(很明显可利用的CPU只有一个)情况下实现并发,先回顾一下并发的本质:切换+保存状态 CPU正在运行一个任务,会在两种情况下切走去执行其它的任务(切换由操作系统强制控制),一种情况是该任务发生了阻塞,另外一种情况是该任务计算的时间过长或有一个优先级更高的程序替代了它 ps:在介绍进程理论时,提及进程的三种执行状态,而线程才是执行单位,所以也可以将上图理解为线程的三种状态 一:其中第二种情况并不能提升效率,只是让CPU能够雨露均沾,实现看起来所有任务

Python学习:python并发编程之协程

本节内容: 1.协程介绍. 2.回顾yield 3.Greenlet 4.Gevent介绍 5.Gevent之同步与异步 6.Gevent之应用举例一 7.Gevent应用举例二 一.引子 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去执行其他的任务(切换由操作系统强制控制),一种情况是该任务发生了阻塞,另外一种情况是该任务计算的时间过长 ps:在介绍进

【PYTHON模块】:协程与greenlet、gevent

协程:又称为微线程,英文名称Coroutine. 作用:它拥有自己的寄存器上下文和栈,能保留上一次调用时的状态,可以随时暂停程序,随时切换回来. 优点: ?无需线程上下文切换的开销    ?无需原子操作锁定及同步的开销 ?方便切换控制流,简化编程模型    ?高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题.所以很适合用于高并发处理 缺点:    ?无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上    ?进行阻塞

Python多任务实现 之协程并发下载多图片

协程是Python中实现多任务一种方式,相比多任务之进程和线程,协程不需要消耗过多的资源,更高效的利用了cpu资源. 在Python中通过gevent封装generator迭代器功能实现多任务的切换.协程在运行过程中是靠程序的耗时操作来实现程序中断.达到切换多任务.至始至终,程序都是在一条主线程里面完成的. 下面是一个利用协程实现多张图片的同时下载. from gevent import monkey import gevent import urllib.request # 设置识别耗时操作

Python 之并发编程之协程

一.协程 ''' def gen(): for i in range(10): yield i # 初始化生成七函数 返回生成器对象,简称生成器 mygen = gen() for i in mygen: print(i) ''' # (1) 用协程改写成生产者消费者 ''' def producer(): for i in range(100): yield i def consumer(): g = producer() for i in g: print(i) ''' # (2) 协程的具

python学习道路(day11note)(协程,同步与异步的性能区别,url爬网页,select,RabbitMq)

1.协程 1 #协程 又称微线程 是一种用户的轻量级线程 程序级别代码控制 就不用加机器 2 #不同函数 = 不同任务 A函数切到B函数没有进行cpu级别的切换,而是程序级别的切换就是协程 yelied 3 4 #单线程下多个任务流用协程,比如打电话可以切换,nginx 5 #爽妹给你打电话的时候,她不说话,刘征电话过来时候你可以切过去,这时候要是爽妹说话,就会bibi响 6 ''' 7 8 协程的好处: 9 无需线程上下文切换的开销 10 无需原子操作锁定及同步的开销 11 "原子操作(ato

python 高性能编程之协程

用 greenlet 协程处理异步事件 自从 PyCon 2011 协程成为热点话题以来,我一直对此有着浓厚的兴趣.为了异步,我们曾使用多线程编程.然而线程在有着 GIL 的 Python 中带来的性能瓶颈和多线程编程的高出错风险,"协程 + 多进程"的组合渐渐被认为是未来发展的方向.技术容易更新,思维转变却需要一个过渡.我之前在异步事件处理方面已经习惯了回调 + 多线程的思维方式,转换到协程还非常的不适应.这几天我非常艰难地查阅了一些资料并思考,得出了一个可能并不可靠的总结.尽管这个