网络编程(七) 并发编程之socket服务端实现并发+协程
一. socket服务端实现并发(开多线程)
import socket from threading import Thread """ 服务端: 1.固定的ip和port 2.24小时不间断提供服务 3.支持高并发 """ server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) # 半连接池 def communicate(conn): while True: try: data = conn.recv(1024) # 阻塞 if len(data) == 0:break print(data) conn.send(data.upper()) except ConnectionResetError: break conn.close() while True: conn,addr = server.accept() # 阻塞 print(addr) t = Thread(target=communicate,args=(conn,)) t.start()
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: info = input('>>>:').encode('utf-8') if len(info) == 0:continue client.send(info) data = client.recv(1024) print(data)
二. 单线程下实现并发:开协程
服务端:
from gevent import monkey;monkey.patch_all() from gevent import spawn import socket def communicate(conn): while True: try: data = conn.recv(1024) if len(data) == 0: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()
# 原本服务端需要开启500个线程才能跟500个客户端通信,现在只需要一个线程就可以扛住500客户端
客户端:
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()
三.进程池线程池
为了减缓计算机硬件的压力,避免计算机硬件设备崩溃, 从而限制开设的进程数和线程数.
提交任务的方式:
同步:提交任务之后,原地等待任务的返回结果,再继续执行下一步代码
异步:提交任务之后,不等待任务的返回结果(通过回调函数拿到返回结果并处理),直接执行下一步操作
回调函数: 异步提交之后一旦任务有返回结果,自动交给另外一个去执行
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import time,os pool=ProcessPoolExecutor(5) def task(n): print(n,os.getpid()) time.sleep(2) return n*2 def call_back(n): print('get result:%s'%n.result()) if __name__ == '__main__': t_list=[] for i in range(20): future=pool.submit(task,i).add_done_callback(call_back) t_list.append(future) print('main')
四.协程: 通过代码层面自己监测io, 自己实现切换,让操作系统误认为这个线程没有io.
那么切换+保存状态就一定能够提升你程序的效率吗?
不一定,当你的任务是计算密集型,反而会降低效率,如果你的任务是IO密集型,会提升效率
from gevent import monkey; monkey.patch_all() #检测代码中所有io行为 from gevent import spawn import time def heng(name): print('%s 哼'%name) time.sleep(2) print('%s 哼' % name) def ha(name): print('%s 哈'%name) time.sleep(3) print('%s 哈' % name) start = time.time() s1 = spawn(heng,'egon') s2 = spawn(ha,'echo') s1.join() s2.join() # heng('egon') # ha('kevin') print('主',time.time()-start)
加协程后原来5s缩短为3s
五.在来回切换过程中,计算密集型的任务执行时间比串行还长
# 串行执行 import time def func1(): for i in range(10000000): i+1 def func2(): for i in range(10000000): i+1 start = time.time() func1() func2() stop = time.time() print(stop - start) # 基于yield并发执行 import time def func1(): while True: 10000000+1 yield def func2(): g=func1() for i in range(10000000): i+1 next(g) start=time.time() func2() stop=time.time() print(stop-start)