并发之协程
Posted guyanzhi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发之协程相关的知识,希望对你有一定的参考价值。
协程 Coroutine
据说, python官方没有协程这个玩意儿 ,这是程序员自己鼓捣出来的, 更加轻量级(任务切换不是由自己的代码实现,而不是操作系统)
也叫微线程, 用这个可以让自己的程序对CPU 的占用率更高!避免被操作系统剥夺CPU的使用权.
对比操作系统控制线程的切换,用户在单线程内控制协程的切换
优点如下:
#1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级 #2. 单线程内就可以实现并发的效果,最大限度地利用cpu
缺点如下:
#1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程 #2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程
那么做到协程起码得实现以下两点:
1. 可以控制多个任务之间的切换,切换之前将任务的状态保存下来,以便重新运行时,可以基于暂停的位置继续执行。 2. 作为1的补充:可以检测IO操作,在遇到io操作的情况下才发生切换
那如何去实现呢?
对于 1 . 可以使用yield 但是yield 不能识别阻塞
import time def func1(): while True: yield def func2(): g=func1() for i in range(10000000): i+1 next(g) start=time.time() func2() stop=time.time() print(stop-start)
如果我们在单个线程内有20个任务,要想实现在多个任务之间切换,使用yield生成器的方式过于麻烦(需要先得到初始化一次的生成器,然后再调用send。。。非常麻烦),而使用greenlet模块可以非常简单地实现这20个任务直接的切换(这是个第三方模块,需要自己安装)
from greenlet import greenlet def eat(name): print(‘%s eat 1‘ %name) g2.switch(‘egon‘) print(‘%s eat 2‘ %name) g2.switch() def play(name): print(‘%s play 1‘ %name) g1.switch() print(‘%s play 2‘ %name) g1=greenlet(eat) g2=greenlet(play) g1.switch(‘jerry‘) # 可以在第一次switch时传入参数,以后都不需要
但是以上两者都不能实现识别到阻塞,并在阻塞前切换到
别慌,肯定有办法的
import gevent, time from gevent import monkey # 帮助监测程序自己的阻塞 def micro_thread1(): print("thread1 run...") print(time.time()) # gevent.sleep(2)# 可以检测到自己的阻塞 monkey.patch_all() print("睡眠") time.sleep(1) # 你以为你用的sleep 实际上间接用的 gevent里的sleep 点patch_all()进去看 print("thread1 run...") def micro_thread2(): print(time.time()) time.sleep(3) # 模拟IO操作耗时 在IO的时候 micro_thread1 还在运行 print("thread2 run...") g1 = gevent.spawn(micro_thread1) g2 = gevent.spawn(micro_thread2) gevent.joinall([g1, g2]) # 以后就都这样写 便不会因为单独对象join()而出错 这里的join兼顾start() 而单独的start是没用的
from gevent import monkey; monkey.patch_all() import gevent import requests import time def get_page(url): print(‘GET: %s‘ % url) response = requests.get(url) if response.status_code == 200: print(‘%d bytes received from %s‘ % (len(response.text), url)) start_time = time.time() gevent.joinall([ gevent.spawn(get_page, ‘https://www.python.org/‘), gevent.spawn(get_page, ‘https://www.yahoo.com/‘), gevent.spawn(get_page, ‘https://github.com/‘), ]) stop_time = time.time() print(‘run time is %s‘ % (stop_time - start_time))
我们可以用threading.current_thread().getName()来查看每个g1和g2,查看的结果为DummyThread-n,即假线程
以上是关于并发之协程的主要内容,如果未能解决你的问题,请参考以下文章