1 协程

1.1协程的概念

  协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程。(其实并没有说明白~)

  我觉得单说协程,比较抽象,如果对线程有一定了解的话,应该就比较好理解了。

  那么这么来理解协程比较容易:

  线程是系统级别的,它们是由操作系统调度;协程是程序级别的,由程序员根据需要自己调度。我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行别的子程序;别的子程序也可以中断回来继续执行之前的子程序,这就是协程。也就是说同一线程下的一段代码<1>执行着执行着就可以中断,然后跳去执行另一段代码,当再次回来执行代码块<1>的时候,接着从之前中断的地方开始执行。

  比较专业的理解是:

  协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

1.2 协程的优缺点

协程的优点:

  (1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)

  (2)无需原子操作锁定及同步的开销

  (3)方便切换控制流,简化编程模型

  (4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

协程的缺点:

  (1)无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

  (2)进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

2 Python中如何实现协程

2.1 yield实现协程  

  前文所述“子程序(函数)在执行过程中可以中断去执行别的子程序;别的子程序也可以中断回来继续执行之前的子程序”,那么很容易想到Python的yield,显然yield是可以实现这种切换的。

使用yield实现协程操作例子:

  1. #! /usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. # Author: "Zing-p"
  4. # Date: 2017/5/12
  5.  
  6. def consumer(name):
  7. print("要开始啃骨头了...")
  8. while True:
  9. print("\033[31;1m[consumer] %s\033[0m " % name)
  10. bone = yield
  11. print("[%s] 正在啃骨头 %s" % (name, bone))
  12.  
  13. def producer(obj1, obj2):
  14. obj1.send(None) # 启动obj1这个生成器,第一次必须用None <==> obj1.__next__()
  15. obj2.send(None) # 启动obj2这个生成器,第一次必须用None <==> obj2.__next__()
  16. n = 0
  17. while n < 5:
  18. n += 1
  19. print("\033[32;1m[producer]\033[0m 正在生产骨头 %s" % n)
  20. obj1.send(n)
  21. obj2.send(n)
  22.  
  23. if __name__ == '__main__':
  24. con1 = consumer("消费者A")
  25. con2 = consumer("消费者B")
  26. producer(con1, con2)

运行的结果:

2.2 greenlet实现协程

  Python的 greenlet就相当于手动切换,去执行别的子程序,在“别的子程序”中又主动切换回来。。。

  1. greenlet协程例子:
  1. #! /usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. from greenlet import greenlet
  5. # greenlet 其实就是手动切换;gevent是对greenlet的封装,可以实现自动切换
  6.  
  7. def test1():
  8. print("")
  9. gr2.switch() # 切换去执行test2
  10. print("")
  11. gr2.switch() # 切换回test2之前执行到的位置,接着执行
  12.  
  13. def test2():
  14. print("")
  15. gr1.switch() # 切换回test1之前执行到的位置,接着执行
  16. print("")
  17.  
  18. gr1 = greenlet(test1) # 启动一个协程 注意test1不要加()
  19. gr2 = greenlet(test2) #
  20. gr1.switch()
  1.  

2.3  gevent 实现协程

  Gevent 是一个第三方库,可以轻松通过gevent实现协程程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

  gevent会主动识别程序内部的IO操作,当子程序遇到IO后,切换到别的子程序。如果所有的子程序都进入IO,则阻塞。

协程之gevent例子:

  1. #! /usr/bin/env python3
  2. # -*- coding:utf-8 -*-
  3.  
  4. import gevent
  5.  
  6. def func1():
  7. print("func1 running")
  8. gevent.sleep(2) # 内部函数实现io操作
  9. print("switch func1")
  10.  
  11. def func2():
  12. print("func2 running")
  13. gevent.sleep(1)
  14. print("switch func2")
  15.  
  16. def func3():
  17. print("func3 running")
  18. gevent.sleep(0)
  19. print("func3 done..")
  20.  
  21. gevent.joinall([gevent.spawn(func1),
  22. gevent.spawn(func2),
  23. gevent.spawn(func3),
  24. ])

同步与异步性能区别:

  1. import gevent
  2.  
  3. def task(pid):
  4. """
  5. Some non-deterministic task
  6. """
  7. gevent.sleep(0.5)
  8. print('Task %s done' % pid)
  9.  
  10. def synchronous():
  11. for i in range(1,10):
  12. task(i)
  13.  
  14. def asynchronous():
  15. threads = [gevent.spawn(task, i) for i in range(10)]
  16. gevent.joinall(threads)
  17.  
  18. print('Synchronous:')
  19. synchronous()
  20.  
  21. print('Asynchronous:')
  22. asynchronous()

  上面程序的重要部分是将task函数封装到greenlet内部线程的gevent.spawn。 初始化的greenlet列表存放在数组threads中,此数组被传给gevent.joinall 函数,后者阻塞当前流程,并执行所有给定的greenlet。执行流程只会在 所有greenlet执行完后才会继续向下走。

遇到Io阻塞时会切换任务之【爬虫版】

  1. #! /usr/bin/env python3
  2. # -*- coding:utf-8 -*-
  3.  
  4. from urllib import request
  5. import gevent,time
  6. from gevent import monkey
  7.  
  8. monkey.patch_all() # 把当前程序中的所有io操作都做上标记
  9.  
  10. def spider(url):
  11. print("GET:%s" % url)
  12. resp = request.urlopen(url)
  13. data = resp.read()
  14. print("%s bytes received from %s.." % (len(data), url))
  15.  
  16. urls = [
  17. "https://www.python.org/",
  18. "https://www.yahoo.com/",
  19. "https://github.com/"
  20. ]
  21.  
  22. start_time = time.time()
  23. for url in urls:
  24. spider(url)
  25. print("同步耗时:",time.time() - start_time)
  26.  
  27. async_time_start = time.time()
  28. gevent.joinall([
  29. gevent.spawn(spider,"https://www.python.org/"),
  30. gevent.spawn(spider,"https://www.yahoo.com/"),
  31. gevent.spawn(spider,"https://github.com/"),
  32. ])
  33. print("异步耗时:",time.time() - async_time_start)
  34.  
  35. # 最好爬国外网站吧

通过gevent实现【单线程】下的多socket并发

server端:

  1. import sys
  2. import socket
  3. import time
  4. import gevent
  5.  
  6. from gevent import socket,monkey
  7. monkey.patch_all()
  8.  
  9. def server(port):
  10. s = socket.socket()
  11. s.bind(('0.0.0.0', port))
  12. s.listen(500)
  13. while True:
  14. cli, addr = s.accept()
  15. gevent.spawn(handle_request, cli)
  16.  
  17. def handle_request(conn):
  18. try:
  19. while True:
  20. data = conn.recv(1024)
  21. print("recv:", data)
  22. conn.send(data)
  23. if not data:
  24. conn.shutdown(socket.SHUT_WR)
  25.  
  26. except Exception as ex:
  27. print(ex)
  28. finally:
  29. conn.close()
  30. if __name__ == '__main__':
  31. server(9999)

client端:

  1. import socket
  2.  
  3. HOST = 'localhost' # The remote host
  4. PORT = 9999 # The same port as used by the server
  5. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  6. s.connect((HOST, PORT))
  7. while True:
  8. msg = bytes(input(">>:"),encoding="utf8")
  9. s.sendall(msg)
  10. data = s.recv(1024)
  11. #print(data)
  12.  
  13. print('Received', repr(data))
  14. s.close()

  觉得将就点个赞~~

Python【第十篇】协程、异步IO的更多相关文章

  1. python 多协程异步IO爬取网页加速3倍。

    from urllib import request import gevent,time from gevent import monkey#该模块让当前程序所有io操作单独标记,进行异步操作. m ...

  2. Python全栈开发-Day10-进程/协程/异步IO/IO多路复用

    本节内容 多进程multiprocessing 进程间的通讯 协程 论事件驱动与异步IO Select\Poll\Epoll——IO多路复用   1.多进程multiprocessing Python ...

  3. Python 8 协程/异步IO

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

  4. Python 协程/异步IO/Select\Poll\Epoll异步IO与事件驱动

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

  5. 12.python进程\协程\异步IO

    进程 创建进程 from multiprocessing import Process import time def func(name): time.sleep(2) print('hello', ...

  6. 线程、进程、协程 异步io

    https://www.cnblogs.com/wupeiqi/articles/6229292.html

  7. 【Python之路】特别篇--事件驱动与异步IO

    通常,我们写服务器处理模型的程序时,有以下几种模型: (1)每收到一个请求,创建一个新的进程,来处理该请求: (2)每收到一个请求,创建一个新的线程,来处理该请求: (3)每收到一个请求,放入一个事件 ...

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

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

  9. 【python】-- 协程介绍及基本示例、协程遇到IO操作自动切换、协程(gevent)并发爬网页

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

  10. Python之路(第四十七篇) 协程:greenlet模块\gevent模块\asyncio模块

    一.协程介绍 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的. 协程相比于线程,最大的区别在于 ...

随机推荐

  1. 撸一个JS正则小工具

    写完正则在浏览器上检测自己写得对不对实在是不方便,于是就撸了一个JS正则小demo出来. demo demo展示 项目地址 代码部分 首先把布局样式先写好. <!DOCTYPE html> ...

  2. js_面向对象编程

    主要内容 值类型与引用类型的特征 深拷贝与浅拷贝 对象的动态特性 构造函数的执行过程 异常处理 dom操作(略) <!DOCTYPE html> <html> <head ...

  3. Python介绍、环境搭建(Eclipse插件)、第一个程序

    Python介绍 特点 优雅.明白.简单. 适合领域 1. Web站点和各种网络服务 2. 系统工具和脚本 3. 作为"胶水"语言把其它语言开发的模块包装起来方便使用 和其它语言对 ...

  4. lambda与函数调用的转换

    14.38 编写一个类令其检查某个给定的string对象的长度是否与一个阈值相等.使用该对象编写程序,统计并报告在输入的文件中长度为1的单词有多少个,长度为2的单词有多少个.......长度为10的单 ...

  5. [转] Putty - 文件夹显示的蓝色太暗

    Putty SSH key的后缀为ppk 默认文件夹的颜色显示为ANSI Blue,颜色太暗. ANSI Blue : RGB(0, 0, 187) 将ANSI Blue修改为和ANSI Blue B ...

  6. 史上最全的iOS各种设备信息获取总结

    来源:si1ence 链接:http://www.jianshu.com/p/b23016bb97af 为了统计用户信息.下发广告,服务器端往往需要手机用户设备及app的各种信息,下面讲述一下各种信息 ...

  7. XC应用系列作品(Android应用)

    XC系列应用,如真题园手机客户端1.1等应用已经分别在 360手机助手.腾讯应用宝.百度手机助手.小米应用商店.豌豆荚.应用汇.木蚂蚁等安卓市场平台上线了! 本页面的系列应用是本人的开发的一Andro ...

  8. CentOS隐藏nginx版本号

    在/usr/local/nginx/conf/目录中,修改nginx.conf文件 在http {  }段中加入 http { server_tokens off; }

  9. JPA entity versioning (@Version and Optimistic Locking)

    详情见: http://www.byteslounge.com/tutorials/jpa-entity-versioning-version-and-optimistic-locking

  10. LA 4329(树状数组)

    题目描述: N <tex2html_verbatim_mark>(3N20000) <tex2html_verbatim_mark>ping pong players live ...