一、进程池线程池(*****)
无论是开进程还是开线程都消耗资源,开线程比开进程消耗的资源要小
1、池:为了减缓计算机硬件的压力,避免计算机硬件设备崩溃。虽然减轻了计算机的压力,但是一定程度上降低了持续的效率
2、为什么要用“池”: 池子使用来限制并发的任务数目,限制我们的计算机在一个自己可承受的范围内去并发地执行任务
3、进程池线程池:为了限制开设的进程数和线程数,从而保证计算机硬件的安全
4、池子内什么时候装进程:并发的任务属于计算密集型
池子内什么时候装线程:并发的任务属于IO密集型
①进程池
from concurrent.futures import ProcessPoolExecutor import time, os, random def task(x): print('%s 接客' % os.getpid()) time.sleep(random.randint(2, 5)) return x**2 if __name__ == '__main__': p = ProcessPoolExecutor(5) # ()内不加参数默认开启的进程数是cpu的核数 for i in range(20): p.submit(task, i)
②线程池
from concurrent.futures import ThreadPoolExecutor import time,random def task(x): print('%s 接客' % x) time.sleep(random.randint(2, 5)) return x ** 2 if __name__ == '__main__': p = ThreadPoolExecutor(4) # ()内不加参数默认开启的线程数是cpu的核数*5 for i in range(20): p.submit(task, i)
二、同步异步阻塞非阻塞(*****)
1、同步与异步:提交任务的两种方式
同步调用:提交完任务后,就在原地等待,直到任务运行完毕后,拿到任务的返回值,才继续执行下一行代码
异步调用:提交完任务后,不在原地等待,直接执行下一行代码
from concurrent.futures import ThreadPoolExecutor import time, random def task(x): print('%s 接客' % x) time.sleep(random.randint(1, 3)) return x ** 2 # 异步调用 if __name__ == '__main__': p = ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5 obj_l = [] for i in range(10): obj = p.submit(task, i) obj_l.append(obj) p.shutdown(wait=True) # 等待所有任务运行完毕在执行结果obj调result(),这里拿结果不同等待 print(obj_l[0].result()) print('主') # 同步调用 if __name__ == '__main__': p = ThreadPoolExecutor(4) # 默认开启的线程数是cpu的核数*5 for i in range(10): res = p.submit(task, i).result() # 提交任务在原地等结果,与阻塞没有关系。 # 拿到结果之后再继续执行 print(res) print('主')
2、阻塞与非阻塞:程序的两种运行状态
阻塞:遇到IO就发生阻塞,程序一旦遇到阻塞操作就会停在原地,并且立刻释放CPU资源
非阻塞(就绪态或运行态):没有遇到IO操作,或者通过某种手段让程序即便是遇到IO操作也不会停在原地执行其他操作,力求尽可能多的占有CPU
三、协程(单线程下实现并发)
1、并发实现的本质:切换+保存状态
2、并发:看起来是同时运行,切换+保存状态
并行:真正意义上的同时运行,只有在多cpu的情况下才能 实现并行,4个cpu能够并行4个任务
串行:一个人完完整整地执行完毕才运行下一个任务
协程:单线程下实现并发,自己通过代码层面监测自己的io行为, 并自己实现切换+保存状态,让操作系统误认为这个线程没有io
①客户端
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(100): t = Thread(target=client) t.start()
②服务端
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()
四、gevent模块使用
from gevent import monkey; monkey.patch_all() 监测代码中所有io行为
from gevent import monkey; monkey.patch_all() # 监测代码中所有io行为 from gevent import spawn # gevent本身识别不了time.sleep等不属于该模块内的io操作 import time def heng(name): print('%s 哼' % name) time.sleep(2) print('%s 哼' % name) def ha(name): print('%s 哈' % name) time.sleep(2) print('%s 哈' % name) start = time.time() s1 = spawn(heng, 'egon') s2 = spawn(ha, 'tank') s1.join() s2.join() print('主', time.time()-start) # 结果为 # egon 哼 # tank 哈 # egon 哼 # tank 哈 # 主 2.1111209392547607