- 协程,又称微线程,协程是一种用户态的轻量级线程。
- 协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前 保存的寄存器上下文和栈。因此,协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时, 就相当于进入上一次调用的状态,换种说法:进入上一次离开时,所处逻辑流的位置。
- 协程的好处:
- 无需线程上下文切换的开销;
- 无需原子操作锁定及同步的开销;
- 方便切换控制流,简化编程模型;
- 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题,所以很适合用于高并发处理。
- 缺点:
- 无法利用多核资源:协程的本质是单个线程,它不能同时将单个CPU的多个核用上,协程需要和进程配合才能运行在多 CPU上,当然我们日常所编写的绝大部分应用都没有这个必要,除非是CPU密集型应用;
- 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序;
# 示例: yield 支持下的协程import timeimport queuedef consumer(name): print('--- 开始吃包子---') while True: new_baozi = yield print('[%s] 正在吃包子 %s' % (name, new_baozi))def producer(): r = con.__next__() r = con2.__next__() n = 0 while n < 5: n += 1 con.send(n) con2.send(n) print('\033[32;1m[producer]\033[0m 正在做包子 %s' % n)if __name__ == '__main__': con = consumer('c1') # 创建一个生成器对象 con con2 = consumer('c2') # 创建另一个生成器对象 con2 p = producer()# 示例二: greenlet 下的协程from greenlet import greenletdef test1(): print('12') gr2.switch() print('34') gr2.switch()def test2(): print('56') gr1.switch() print('78')gr1 = greenlet(test1)# print(gr1) #gr2 = greenlet(test2)gr1.switch()# 示例三: gevent 下的协程# Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet,# 它是以C扩展模块形式接入Python的轻量级协程。import geventdef foo(): print('Running in foo') gevent.sleep(1) print('Explicit context switch to foo again.')def bar(): print('Explicit context to bar') gevent.sleep(1) print('Implicit context switch back to bar.')gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar),])# 示例四: 通过 gevent 实现单线程下的多 socket 并发# server 端import geventfrom gevent import socketdef server(port): s = socket.socket() s.bind(('0.0.0.0', port)) s.listen(500) while True: cli, addr = s.accept() gevent.spawn(handle_request, cli)def handle_request(conn): try: while True: data = conn.recv(1024) print('recv:', data) conn.send(data) if not data: conn.shutdown(socket.SHUT_WR) except Exception as ex: print(ex) finally: conn.close()if __name__ == '__main__': server(8001)# client 端import socketHOST = 'localhost'PORT = 8001s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect((HOST, PORT))while True: msg = bytes(input('>>>:'), encoding = 'utf8') s.sendall(msg) data = s.recv(1024) print('Received', repr(data))s.close()
参考资料: