python之路---并发编程之协程&gevent模块
python之路---并发编程之协程&gevent模块
协程
想要在单线程下实现并发,又称之为微线程/纤程,是一种用户态的轻量级线程(由应用程序自己控制调度)
如何在单线程下实现并发
什么是并发
看起来像同时运行(实际上同一时刻只有一个任务在运行),本质是:操作系统控制cpu进行切换+保存状态
程序运行的三种状态
阻塞:当任务出现IO时,发生阻塞(cpu被操作系统剥离)
就绪:当发生阻塞的任务出现有效输出时,由阻塞进入就绪/当一个优先级更高的任务出现时,由运行进入就绪态
运行:当处于就绪态的任务被分配到cpu时,由就绪进入运行
此外
1.一个任务不可能一直占用着CPU,我们能控制的就是将程序尽可能的保持在就绪态--->减少IO
2.遇到IO的切换是提升效率的,单纯的IO切换反而降低效率
我们说一个C/S架构的套接字软件,其服务端应该满足支持并发,对于这种多IO的任务,应该使用多线程,但是,这种并发的实现没有解决IO问题,当一个线程出现IO时,整个线程就发生了阻塞(只是没有影响其它线程而已)
如何实现利用率的最大化
通过在一个线程内多个任务的切换+保存状态(在应用程序级别,而非操作系统级别实现,由一个正在IO的任务切换到其它计算的任务) --->这样使单个线程时刻保持在就绪状态(将线程内的IO行为隐藏,让操作系统尽可能的将cpu分配给自己)
注意
1.python的线程是属于内核级别的,由操作系统控制,调度(进行强制切换),而线程是应用程序级别的,由应用程序自己控制调度
2.操作系统只有线程与进程的概念(操作系统调度的是线程)
优点
1.应用程序级别的切换开销更小
2.在单线程下实现并发,最大限度的利用CPU
缺点
1.一旦引入协程的概念,就得监测一个任务下的所以IO行为,实现遇到IO就切换,一旦有一个地方没切,整个线程就阻塞在了原地
2.本质还是一个线程,无法利用多核
如何实现单线程下的并发
1.可以利用python下的gevent模块
2.IO模型
对于使用gevent模块,首先要下载
pip insatll gevent
from gevent import monkey;monkey.patch_all() import gevent import time def task1(): print('task1 is running...') time.sleep(1) print('task1 is done...') def task2(): print('task2 is running...') time.sleep(3) print('task2 is done...') if __name__ == '__main__': p1 = gevent.spawn(task1) p2 = gevent.spawn(task2) # 异步提交任务 gevent.joinall([p1,p2]) print('>>>')利用协程实现单线程下的简单并发套接字
# Server from socket import * from gevent import monkey, spawn monkey.patch_all() sever_address = ('127.0.0.1', 8080) def task(conn): while True: try: data = conn.recv(1024) if len(data) == 0:break conn.send(data.upper()) except ConnectionResetError: break def build(): socket_obj = socket(AF_INET,SOCK_STREAM) socket_obj.bind(sever_address) socket_obj.listen() print('server is reading...') while True: conn, client_address = socket_obj.accept() print('client is arrive...',client_address) spawn(task,conn) if __name__ == '__main__': obj=spawn(build) obj.start() obj.join()
# Client from socket import * from threading import Thread,current_thread def task(address): obj = socket(AF_INET,SOCK_STREAM) obj.connect(address) while True: print(current_thread().name) obj.send(b'hello world') for i in range(500): t = Thread(target=task,args=(('127.0.0.1',8080),)) t.start()
浙公网安备 33010602011771号