一. 进程池与线程池
在刚开始学多进程或多线程时,我们迫不及待地基于多进程或多线程实现并发的套接字通信,然而这种实现方式的致命缺陷是:服务的开启的进程数或线程数都会随着并发的客户端数目地增多而增多,这会对服务端主机带来巨大的压力,甚至于不堪重负而瘫痪,于是我们必须对服务端开启的进程数或线程数加以控制,让机器在一个自己可以承受的范围内运行,这就是进程池或线程池的用途,例如进程池,就是用来存放进程的池子,本质还是基于多进程,只不过是对开启进程的数目加上了限制.
1 from socket import * 2 from threading import Thread 3 4 5 def connect(conn): 6 while True: 7 try: 8 data=conn.recv(8192) 9 print(data.decode("utf-8").upper()) 10 if not data: 11 break 12 conn.send(data.upper()) 13 except ConnectionResetError: 14 break 15 conn.close() 16 17 18 def server(ip,port): 19 ser=socket(AF_INET,SOCK_STREAM) 20 ser.bind((ip,port)) 21 ser.listen(5) 22 print("server is running") 23 while True: 24 conn,addr=ser.accept() 25 t=Thread(target=connect,args=(conn,)) 26 t.start() 27 28 ser.close() 29 30 31 if __name__=='__main__': 32 server("127.0.0.1",19980)
1 from socket import * 2 3 client = socket(AF_INET,SOCK_STREAM) 4 client.connect(("127.0.0.1",19980)) 5 6 while True: 7 inp = input(">>>>").strip() 8 if not inp: 9 continue 10 client.send(inp.encode("utf-8")) 11 data = client.recv(8192) 12 print(data.decode("utf-8")) 13 client.close()
在上面的套接字通信中,客户端来一个请求,服务器就开一个线程.如果来了1000个请求,服务器就开1000线程.这样就对服务器端造成很大的负担,很可能就造成宕机.这时候,我们就可以用到线程池了.
1 from socket import * 2 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor 3 4 5 def connect(conn): 6 while True: 7 try: 8 data=conn.recv(8192) 9 print(data.decode("utf-8").upper()) 10 if not data: 11 break 12 conn.send(data.upper()) 13 except ConnectionResetError: 14 break 15 conn.close() 16 17 18 def server(ip,port): 19 ser=socket(AF_INET,SOCK_STREAM) 20 ser.bind((ip,port)) 21 ser.listen(5) 22 print("server is running") 23 while True: 24 conn,addr=ser.accept() 25 pool.submit(connect,conn) 26 27 ser.close() 28 29 30 if __name__=='__main__': 31 pool = ThreadPoolExecutor(max_workers=3) 32 server("127.0.0.1",19980)
1 from socket import * 2 3 client = socket(AF_INET,SOCK_STREAM) 4 client.connect(("127.0.0.1",19980)) 5 6 while True: 7 inp = input(">>>>").strip() 8 if not inp: 9 continue 10 client.send(inp.encode("utf-8")) 11 data = client.recv(8192) 12 print(data.decode("utf-8")) 13 client.close()
二. 线程池和进程池的基本方法
1.submit(fn, *args, **kwargs):异步提交任务
2.map(func, *iterables, timeout=None, chunksize=1) :取代for循环的submi
3.shutdown(wait=):
相当于进程池的pool.close()+pool.join()操作
wait=True,等待池内所有任务执行完毕回收完资源后才继续
wait=False,立即返回,并不会等待池内的任务执行完毕
但不管wait参数为何值,整个程序都会等到所有任务执行完毕
submit和map必须在shutdown之前
4.result(timeout=None):取得结果
5.add_done_callback(fn):回调函数