Day 36 socket并发,协程,进程池与线程池
一、socket并发
#服务端
import socket from threading import Thread server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) def communcate(conn): while True: try: data = conn.recv(1024) if not data: break print(data) conn.send(data.upper()) except ConnectionResetError: break conn.close() while True: conn,addr=server.accept() print(addr) t=Thread(target=communcate,args=(conn,)) t.start()
#客户端 import socket client = socket.socket() client.connect(('127.0.0.1', 8080)) while True: info = input(">>>:").encode('utf-8') if not info:continue client.send(info) data=client.recv(1024) print(data.decode('utf-8'))
二、协程
协程:
进程:资源单位(车间)
线程:最小执行单位(流水线)
协程:单线程下实现并发
并发:看上去像同时执行就可以称之为并发
多道技术:
空间上的复用
时间上的复用
核心:切换+保存状态
协程:完全是我们高技术的人自己编出来的名词
通过代码层面自己监测io自己实现切换,让操作系统误认为
你这个线程没有io
切换+保存状态就一定能够提升你程序的效率吗?
不一定
当你的任务是计算密集型,反而会降低效率
如果你的任务是IO密集型,会提升效率
协程:单线程下实现并发
如果你能够自己通过代码层面监测你自己的io行为
并且通过代码实现切换+保存状态
串行执行纯计算操作
import time def task1(): res =1 for i in range(1000000): res+=i def task2(): res=1 for i in range(1000000): res*=i stat=time.time() task1() task2() stop =time.time() print(stop-stat)
计算一段时间往上切,加上了来回切的时间
基于yiel保存状态,实现两个任务间来回切换,实现并发效果
yield并不能帮我们自动捕获到io行为才切换
import time def task1(): res =1 for i in range(1000000): res+=i yield def task2(): g=task1() res=1 for i in range(1000000): res*=i next(g) stat=time.time() task2() stop =time.time() print(stop-stat)
三、gevent模块
一个spawn就是一个帮你管理任务的对象
gevent模块不能识别它本身以外的所有的IO行为比如(time.sleep)
from gevent import monkey;monkey.patch_all() #能够帮助我们识别所有的IO行为 from threading import current_thread import gevent import time def eat(): print('%s eat 1'%current_thread().name) # gevent.sleep(3) time.sleep(3) print('%s eat 2'%current_thread().name) def play(): print('%s play 1'%current_thread().name) # gevent.sleep(5) time.sleep(5) print('%s play 2'%current_thread().name) g1 =gevent.spawn(eat) #异步提交 g2 =gevent.spawn(play) print(current_thread().name) g1.join() g2.join()
四、进程池与线程池
1、为什么要用“池”:
池子是用来限制并发的任务数目,限制我们的计算机在一个自己承受的范围内去并发的执行任务
2、什么时候用进程池:
并发的任务属于计算密集型
3、什么时候用线程池:
并发的任务属于IO密集型
#进程池 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import time,os,random def task(x): print('%s 接客'%os.getpid()) time.sleep(random.randint(2,3)) return x**2 if __name__ == '__main__': p =ProcessPoolExecutor(max_workers=4) #默认开启进程数是cpu的核数 for i in range(20): p.submit(task,i) #submit提交任务的方式,异步提交
#线程池 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import time,os,random def task(x): print('%s 接客'%x) time.sleep(random.randint(2,3)) return x**2 if __name__ == '__main__': p =ThreadPoolExecutor(4) #默认开启(线程数=cpu核数*5左右) for i in range(20): p.submit(task,i) #submit提交任务的方式,异步提交
五、单线程下实现并发
#服务端 from gevent import monkey;monkey.patch_all() from gevent import spawn import socket def communicate(conn): while True: try: data =conn.recv(1024) if not data:break print(data.decode('utf-8')) conn.send(data.upper()) except ConnectionResetError: break conn.close() def server(): server=socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn,addr = server.accept() spawn(communicate,conn) if __name__ == '__main__': s1=spawn(server) s1.join()
#客户端 from threading import Thread,current_thread import socket def client(): client=socket.socket() client.connect(('127.0.0.1',8080)) n=1 while True: data = '%s %s'%(current_thread().name,n) n+=1 client.send(data.encode('utf-8')) info=client.recv(1024) print(info) if __name__ == '__main__': for i in range(500): t=Thread(target=client) t.start()