线程进程池、协程、IO模型
一、进程池与线程池
开进程开线程都需要消耗资源,只不过两者比较的情况线程消耗的资源比较少
在计算机能够承受范围之内最大限度的利用计算机,就要涉及到一个新的概念,线程池和进程池
1.什么是池?
在保证计算机硬件安全的情况下最大限度的利用计算机
池其实是降低了程序的运行效率,但是保证了计算机硬件的安全
(硬件的发展跟不上软件的速度)
2.线程池和进程池
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor 2 import time 3 4 pool = ThreadPoolExecutor(5) # 括号内可以传参数指定线程池内的线程个数,也可以不传,不传默认是当前所在计算机的cpu个数乘5 5 6 ''' 7 池子中创建的进程/线程创建一次就不会再创建了 8 至始至终用的都是最初的那几个 9 这样的话节省了反复开辟进程/线程的资源 10 ''' 11 12 def task(n): 13 print(n) 14 time.sleep(1) 15 return n * 5 16 ''' 17 提交任务的方式 18 同步:提交任务之后,原地等待任务的返回结果,期间不做任何事 19 异步:提交任务之后,不等待任务的返回结果,直接执行下一行代码 20 ''' 21 # pool.submit(task,1) # 朝线程池中提交任务 属于异步提交 22 23 t_list = [] 24 for i in range(20): 25 res = pool.submit(task,i) 26 # print(res.result()) # 原地等待任务的返回结果 27 t_list.append(res) 28 29 pool.shutdown() # 关闭池子,等待池子中所有的任务执行完毕之后,才会往下走 30 for p in t_list: 31 print('函数返回结果为:',p.result())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor 2 import time 3 import os 4 5 pool = ProcessPoolExecutor(5) # 括号内可以传参数指定线程池内的线程个数,也可以不传,不传默认是当前所在计算机的cpu个数乘5 6 ''' 7 池子中创建的进程/线程创建一次就不会再创建了 8 至始至终用的都是最初的那几个 9 这样的话节省了反复开辟进程/线程的资源 10 ''' 11 12 def task(n): 13 print(n,os.getpid()) 14 time.sleep(1) 15 return n * 5 16 ''' 17 提交任务的方式 18 同步:提交任务之后,原地等待任务的返回结果,期间不做任何事 19 异步:提交任务之后,不等待任务的返回结果,直接执行下一行代码 20 ''' 21 # pool.submit(task,1) # 朝线程池中提交任务 属于异步提交 22 23 if __name__ == '__main__': 24 t_list = [] 25 for i in range(20): 26 res = pool.submit(task,i) 27 # print(res.result()) # 原地等待任务的返回结果 28 t_list.append(res) 29 30 pool.shutdown() # 关闭池子,等待池子中所有的任务执行完毕之后,才会往下走 31 for p in t_list: 32 print('函数返回结果为:',p.result())
以上版本并不能实现真正的一一对应,并不够完善,只是让我们感觉上是一个对应一个,想要一一对应的实现,就要用到异步回调机制:当异步提交的任务有返回结果的之后,就会自动触发回调函数的执行
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from concurrent.futures import ProcessPoolExecutor 2 import time 3 import os 4 5 pool = ProcessPoolExecutor(5) # 括号内可以传参数指定线程池内的线程个数,也可以不传,不传默认是当前所在计算机的cpu个数乘5 6 7 def task(n): 8 print(n,os.getpid()) 9 time.sleep(1) 10 return n * 5 11 12 def call_back(n): 13 print('拿到了异步提交的结果:',n.result()) 14 15 16 if __name__ == '__main__': 17 for i in range(20): 18 res = pool.submit(task,i).add_done_callback(call_back) # 提交任务的时候 绑定一个回调函数,一旦该任务有结果,立刻执行对应的回调函数
二、协程
单线程下实现并发
首先捋一捋所有的‘程’:
1.进程:资源单位
2.线程:执行单位
3.协程:单线程下实现并发,它是程序员们自己发明出来的一个词而已,并不是一个什么概念
并发:切换+保存状态,让我们看起来好像就是同时执行的,就可以称之为并发
并发的条件:
多道技术:1.空间上的复用,2.时间上的复用
★协程的真面目:
程序员自己通过代码自己检测程序中的IO,一旦遇到IO自己通过代码切换,给操作系感觉是你这个线程没有任何IO
其实就是,欺骗操作系统,让它误认为你这个程序一直没有IO,从而保证程序在运行态和就绪态来回切换,提升代码的运行效率
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # 串行执行 0.8540799617767334 2 import time 3 4 def func1(): 5 for i in range(10000000): 6 i+1 7 8 def func2(): 9 for i in range(10000000): 10 i+1 11 12 start = time.time() 13 func1() 14 func2() 15 stop = time.time() 16 print(stop - start)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #基于yield并发执行 1.3952205181121826 2 import time 3 def func1(): 4 while True: 5 10000000+1 6 yield 7 8 def func2(): 9 g=func1() 10 for i in range(10000000): 11 time.sleep(100) # 模拟IO,yield并不会捕捉到并自动切换 12 i+1 13 next(g) 14 15 start=time.time() 16 func2() 17 stop=time.time() 18 print(stop-start)
但是上述的切换+保存不能识别你的IO操作,那么就需要一个能够识别IO的工具,一个新的模块:gevent(需要额外下载)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 def heng(): 2 print('哼') 3 time.sleep(2) 4 print('哼') 5 6 def ha(): 7 print('哈') 8 time.sleep(3) 9 print('哈') 10 11 start = time.time() 12 g1 = spawn(heng) 13 g2 = spawn(ha) # spawm会检测所有的任务 14 g1.join() 15 g2.join() 16 print(time.time()-start)
三、使用gevent实现TCP单线程并发通信
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from gevent import monkey;monkey.patch_all() 2 import socket 3 from gevent import spawn 4 5 server = socket.socket() 6 server.bind(('127.0.0.1',8080)) 7 server.listen(5) 8 9 def talk(conn): 10 while True: 11 try: 12 data = conn.recv(1024) 13 if len(data) == 0:break 14 print(data.decode('utf-8')) 15 conn.send(data.upper()) 16 except ConnectionResetError as e: 17 print(e) 18 break 19 conn.close() 20 21 def server1(): 22 while True: 23 conn,addr = server.accept() 24 spawn(talk,conn) 25 26 if __name__ == '__main__': 27 g1 = spawn(server1) 28 g1.join()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import socket 2 from threading import Thread,current_thread 3 4 def client(): 5 client = socket.socket() 6 client.connect(('127.0.0.1',8080)) 7 8 n = 0 9 while True: 10 data = '%s %s'%(current_thread().name,n) 11 client.send(data.encode('utf-8')) 12 res = client.recv(1024) 13 print(res.decode('utf-8')) 14 n += 1 15 16 for i in range(400): 17 t = Thread(target=client) 18 t.start()
四、IO模型
此处引用: