协程,又称微线程,纤程。英文Coroutine。协程是一种用户态的轻量级线程。
线程是系统级别,由操作系统调度;协程是程序级别,由程序员根据需要自行调度。我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行其他的子程序;其他的子程序也可以中断回来继续执行之前的子程序,此之谓协程。
比较专业的理解:
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复之前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时逻辑流所处的位置。
yield实现实例
def consumer(): r = '' while True: n = yield r if not n: return print(f'[consumer] consuming {n}') r = '2 000K' def producer(c): c.send(None) n = 0 while n < 5: n = n + 1 print(f'[producer] producing {n}') r = c.send(n) print(f'[producer] consuming return: {r}') c.close() c = consumer() producer(c) import collections import queue import random ****************************************************************************** Event = collections.namedtuple('Event', 'time proc action') def taxi_process(proc, trips_num, start_time=0): """ :param proc: :param trips_num: :param start_time: :return: """ time = yield Event(start_time, proc, 'leave garage') for i in range(trips_num): time = yield Event(time, proc, 'pick up people') time = yield Event(time, proc, 'drop off people') yield Event(time, proc, 'go home') class SimulateTaxi(): """ 模拟出租车控制台 """ def __init__(self, proc_map): # 保存排定事件的PriorityQueue对象 # 如果是tuple类型,默认使用tuple[0] self.events = queue.PriorityQueue() # procs_map 是dict类型,使用dict构建本地副本 self.procs = dict(proc_map) def run(self, end_time): """ 排定并打印事件流程,直到时间结束 :param end_time: :return: """ for _, taxi_gen in self.procs.items(): leave_evt = next(taxi_gen) self.events.put(leave_evt) # 仿真系统主循环 simulate_time = 0 while simulate_time < end_time: if self.events.empty(): print('*** end of events ***') break # 第一个事件 current_evt = self.events.get() simulate_time, proc, action = current_evt print(f'taxi: {proc}, at time: {simulate_time}, {action}') # 准备下个事件发生 proc_gen = self.procs[proc] next_simulate_time = simulate_time + self.compute_duration() try: next_evt = proc_gen.send(next_simulate_time) except StopIteration: del self.procs[proc] else: self.events.put(next_evt) else: msg = '*** end of simulate' print(msg.format(self.events.qsize())) @staticmethod def compute_duration(): """ 随机产生下个事件发生时间 :return: """ duration_time = random.randint(1, 20) return duration_time # 生成3个出租车,现在全部没有离开车库 taxies = {i: taxi_process(i, (i + 1) * 2, i *5) for i in range(3)} # 模拟运行 st = SimulateTaxi(taxies) st.run(100)
******************************************************************
greenlet实现
greenlet需要手动切换去执行其他的子程序,在其他的子程序中又主动切换回来。
#! /usr/bin/env python
# -*- coding:utf-8 -*-
from greenlet import greenlet
# greenlet 其实就是手动切换;gevent是对greenlet的封装,可以实现自动切换
def test1():
print("123")
gr2.switch() # 切换去执行test2
print("456")
gr2.switch() # 切换回test2之前执行到的位置,接着执行
def test2():
print("789")
gr1.switch() # 切换回test1之前执行到的位置,接着执行
print("666")
gr1 = greenlet(test1) # 启动一个协程 注意test1不要加()
gr2 = greenlet(test2) #
gr1.switch()
gevent实现
gevent是一个三方库,可以轻松通过gevent实现协程,在gevent中用到的主要模式Greenlet,它以C扩展模块形式接入Python轻量级协程。Greenlet运行于主程序操作系统内部,但它们被协作式调度。
gevent会主动识别程序内部IO操作,当子程序遇到IO时,切换至其他的子程序。如果所有的子程序都进入IO,则阻塞。
实例
import gevent def func1(): print('func1 running') gevent.sleep(5) # 内部实现IO print('switch func1') def func2(): print('func2 running') gevent.sleep(3) # 内部实现IO print('switch func2') def func3(): print('func1 running') gevent.sleep(0) # 内部实现IO print('switch func3') gevent.joinall([gevent.spawn(func1), gevent.spawn(func2), gevent.spawn(func3), ])
gevent爬虫版
import requests import gevent, time from gevent import monkey monkey.patch_all() # 把当前程序中所有io操作都做上标记 def spider(url): print('Get {}'.format(url)) res = requests.get(url) data = res.text print('{}s bytes received from {}...'.format(len(data), url)) urls = [ "https://www.python.org/", "https://www.yahoo.com/", "https://github.com/" ] start_time = time.time() for url in urls: spider(url) print('同步耗时: ',time.time()-start_time) start_time = time.time() gevent.joinall([gevent.spawn(spider, url) for url in urls]) print('异步耗时: ',time.time()-start_time)
socket版
server
# import socket import gevent from gevent import socket, monkey monkey.patch_all() def server(port): s = socket.socket() s.bind(('127.0.0.1', port)) s.listen(10) print('开始服务...') while True: conn, addr = s.accept() gevent.spawn(handle_request, conn) def handle_request(conn): try: while True: data = conn.recv(1024).decode() print('recv: ',data) reply = input('>>:') conn.send(reply.encode()) if not data: conn.shutdown(socket.SHUT_WR) except Exception as e: print(e) finally: conn.close() if __name__ == '__main__': server(8888)
client
import socket client = socket.socket() client.connect(('localhost', 8888)) while True: msg = input('>>:') client.send(msg.encode()) data = client.recv(1024).decode() print('server: ', data) client.close()
转自:https://www.cnblogs.com/zingp/p/5911537.html 协程及Python中的协程