单线程并发:切换(任务I/O阻塞,该任务计算时间过长或有一个更高的优先级取代)+保存状态
对于第二种,可用生成器yield,next,send或greenlet来实现并发(greenlet切换比yield方便),并不能提高效率。
对于第一种,用gevent模块,实现I/O切换,提高效率。
协程(微线程):单线程下的并发,用户(应用程序)级别的调度。
(Python的线程是内核级别的,操作系统级别的调度)
协程的特点:
- 必须在只有一个单线程里实现并发
- 修改共享数据不需加锁
- 用户程序里自己保存多个控制流的上下文栈
- 附加:一个协程遇到IO操作自动切换到其它协程。
程序:
# 用generator的send,yield来实现任务的切换 # yield不能实现I/O的切换, 不能提高速率。 def read(): i = 0 while True: print("haha") x = yield print(x) def write(): g = read() next(g) for i in range(10): g.send(i) # 切换,发送信息 write() # pip3 install greenlet 安装 # greenletgreenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务 # 执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。 from greenlet import greenlet # 调用 import time def eat(name): print("%s eat1" % name) time.sleep(5) # 不能自动识别并进行I/O切换 g2.switch("haha") print("%s eat2" % name) g2.switch() pass def play(name): print("%s play1" % name) g1.switch() # 切换到g1 print("%s play2" % name) pass g1 = greenlet(eat) # 创建greenlet的对象 g2 = greenlet(play) g1.switch("alex") # 给g1发信息,并切换到g1 # pip3 install gevent 模块 # 是异步提交任务 # 遇到IO阻塞时会自动切换任务 from gevent import monkey monkey.patch_all() # 实际中可能会遇gevent无法识别的阻塞(如gevent.sleep换为time.sleep), # 所以应直接将下面的代码打个补丁,用上两行代码可以实现. import gevent # 调用 import time def haha(name): print("%s haha1" % name) gevent.sleep(3) print("%s haha2" % name) def kuku(name): print("%s kuku1" % name) time.sleep(5) print("%s kuku2" % name) return "kuku" start_time = time.time() g1 = gevent.spawn(haha, "lulu") # 创建gevent的对象,可以传参数 2,x = 1 g2 = gevent.spawn(kuku, "lulu") # g1.join() # g2.join() gevent.joinall([g1, g2]) # 等待列表中的协程都执行完毕。 # 要是不加的话,主线程执行完毕,其下的协程也会立即结束 print("主") print(time.time() - start_time) # 耗时5.04s,实现了I/O口的自动切换 result1 = g1.value # 获取相应函数的返回值 result2 = g2.value print(result1, result2)