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来实现协程完成真个工作的:

 def main():
foreman(args_of_overall,worker_num) def foreman(args_of_overall,worker_num):
pipeline = create_pipeline(args_of_pipeline,worker_num)
for i,job in enumerate(get_jobs(args_of_ceate_jobs)):
worker_id = i % worker_num
pipeline.send((job,worker_id)) @coroutine
def worker(pipeline,accepting,job,my_id):
while True:
args_of_job, worker_id = (yield )
if worker_id == my_id:
result = work(args_of_job)
accepting.send(result)
elif pipeline is not None:
pipeline.send((job,worker_id)) @coroutine
def accept():
while True:
result = (yield )
#do_some_accepting def create_pipeline(args_of_pipeline,worker_num):
pipeline = None
accepting = accept()
for work_id in range(work_num):
pipeline = worker(pipeline,accepting,job,work_id)
return pipeline def get_jobs(args_of_ceate_jobs):
for job in job_source:
yield job def coroutine(func):
def warper(*args):
f = func(*args)
f.next()
return f
return warper def work(args_of_job):
pass
#do_some_work if __name__ == '__main__':
main()

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

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

Python并发实践_02_通过yield实现协程的更多相关文章

  1. Python并发编程二(多线程、协程、IO模型)

    1.python并发编程之多线程(理论) 1.1线程概念 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程(流水线的工作需要电源,电源就相当于 ...

  2. python并发编程-进程池线程池-协程-I/O模型-04

    目录 进程池线程池的使用***** 进程池/线程池的创建和提交回调 验证复用池子里的线程或进程 异步回调机制 通过闭包给回调函数添加额外参数(扩展) 协程*** 概念回顾(协程这里再理一下) 如何实现 ...

  3. python 并发专题(六):协程相关函数以及实现(gevent)

    文档资源 http://sdiehl.github.io/gevent-tutorial/ 一.协程实现 线程和协程 既然我们上面也说了,协程也被称为微线程,下面对比一下协程和线程: 线程之间需要上下 ...

  4. python教程:使用 async 和 await 协程进行并发编程

    python 一直在进行并发编程的优化, 比较熟知的是使用 thread 模块多线程和 multiprocessing 多进程,后来慢慢引入基于 yield 关键字的协程. 而近几个版本,python ...

  5. Python开发【第九篇】:协程、异步IO

    协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是协程,协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切换回 ...

  6. Python、进程间通信、进程池、协程

    进程间通信 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 进程队列queue 不同于线程queue,进程 ...

  7. (转)Python黑魔法 --- 异步IO( asyncio) 协程

    转自:http://www.jianshu.com/p/b5e347b3a17c?from=timeline Python黑魔法 --- 异步IO( asyncio) 协程 作者 人世间 关注 201 ...

  8. 深入浅析python中的多进程、多线程、协程

    深入浅析python中的多进程.多线程.协程 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源 ...

  9. python基础之进程、线程、协程篇

    一.多任务(多线程) 多线程特点:(1)线程的并发是利用cpu上下文的切换(是并发,不是并行)(2)多线程执行的顺序是无序的(3)多线程共享全局变量(4)线程是继承在进程里的,没有进程就没有线程(5) ...

随机推荐

  1. 浏览器出现Cannot set property 'onclick' of null的问题

    Part1: 当js文件放在head里面时,如果绑定了onclick事件,就会出现这样的错误, 是因为W3School的写法是浏览器先加载完按钮节点才执行的js,所以当浏览器自顶向下解析时,找不到on ...

  2. C#中级-从零打造基于Socket在线升级模块

    一.前言       前段时间一直在折腾基于Socket的产品在线升级模块.之前我曾写过基于.Net Remoting的.基于WCF的在线升级功能,由于并发量较小及当时代码经验的不足一直没有实际应用. ...

  3. VMware---添加esxi主机的实验

     实验环境:window_server_2012_r2_64 SQLServer2012SP1-FullSlipstream-CHS-x64 虚机配置: 关于windows server2012 和 ...

  4. Android - shape圆形画法(oval)

    shape圆形画法(oval) 本文地址: http://blog.csdn.net/caroline_wendy 1. 创建一个目录drawable, 用于存放xml类型的图片资源; 2. 在dra ...

  5. 学习vi和vim编辑器(5):越过基础的藩篱

    本章将对之前学习的编辑命令如" c "." d "." y "等命令进行总结,并学习一些新的知识:其它进入vi的方法,利用缓冲区来存储拖曳或 ...

  6. Maven构建真正的J2EE项目

    今天同事问起我眼下用Maven构建的多模块项目架构和曾经用Eclipse创建的Web项目的问题.以下将讲一下使用maven搭建多模块的J2ee项目,以及採用这样的方式搭建项目对日后项目的水平拆分和垂直 ...

  7. java多线程编程核心技术——第六章总结

    目录 1.0立即加载/"饿汉式" 2.0延迟加载/"懒汉式" 3.0使用静态内置类实现单例模式 4.0序列化与反序列化的单例模式实现 5.0使用static代码 ...

  8. Spring学习笔记(三)之装配Bean

    除了组件扫描与自动装配之外还有基于Java代码的装配与基于XML的装配. 有一些场景是我们不能用自动装配的,比如我们要给第三方库中的组件装配到我们的应用中,这时自动装配无效,因为自动装配只能扫描本应用 ...

  9. Android开发——使用LitePal开源数据库

    前言:之前使用Android内置的数据库,感觉一大堆SQL语句,一不小心就错了,很难受,学习了这个LItePal的开源数据库,瞬间觉得Android内置的数据库简直是垃圾般的存在 LitePal Gi ...

  10. ES6函数的拓展

    ES里面现在支持在函数的参数直接给参数赋一个默认值,ES6支持拓展运算符(...)三个英文的点,这个形式如function(...a)这个里面...a可以接受若干的值,这个拓展运算符也可以把若干的值转 ...