进程池和线程池
进程池线程池:硬件有极限,为了减轻硬件压力,就有了池的概念
#进程池例子
from concurrent.futures import ProcessPoolExecutor
import os, time
pool = ProcessPoolExecutor(5) # 实例化一个进程池对象,里面可以存放5个进程
lis=[]
def task(n):
time.sleep(1)
print(n,'is running',os.getpid())
return n ** 2
def call_back(mes):
print(mes.result(), os.getpid())
if __name__ == '__main__':
for i in range(10):
future = pool.submit(task, i)
future.add_done_callback(call_back) #回调,运行结束自动执行call_back函数
pool.shutdown() #关闭进程池,等待所有进程结束
print('main')
#线程池例子
from concurrent.futures import ThreadPoolExecutor
from threading import current_thread
import time
pool=ThreadPoolExecutor(5) #实例化一个线程池对象,里面可以存放5个线程
def task(n):
print(n,current_thread().name,' is running')
time.sleep(1)
return n**3
def call_back(n):
print(n.result(),current_thread().getName())
for i in range(10):
future=pool.submit(task)
future.add_done_callback(call_back)
pool.shutdown()
print('main')
协程
协程:单线程下实现并发(能够在多个任务直接切换并保存状态),这里注意区分操作系统的切换+保存状态是针对多个线程而言,而我们现在是想在单个线程下实现操作系统切换+保存状态的功能
协程在操作系统中并不存在,它是程序员想象出来的概念,操作系统只有线程与进程。在遇到IO时候切换可以提高效率,但是没有遇到IO仍然切换,这样就额外增加了切换时间,降低了效率。
将单个线程的效率提升到最高,再配合多进程与多线程,实现高并发
方法一:yield
yield虽然可以配合.next(),来回切换,实现并发,但是yield并不能帮我们捕捉到IO行为,而且相比函数的直接调用,增加了切换时间
方法二:gevent模块
一个spawn就是帮助管理任务的对象
注意:gevent模块并不能识别它本身以外(如gevent.sleep(sec))的所有IO行为,但是内部封装了一个模块(monkey),可以帮助我们识别所有的IO行为(所以要提前导入)
from gevent import monkey;monkey.patch_all() #检测所有的IO行为
from gevent import spawn,joinall #joinall列表中放入多个对象,实现join的效果
import time
def part1(s):
print('%s 正在啃鸡腿'%s)
time.sleep(3)
print('%s 正在喝牛奶'%s)
def part2(s):
print('%s 正在吃冰激凌'%s)
time.sleep(5)
print('%s 吃饱了'%s)
start=time.time()
p1=spawn(part1,'wo')
p2=spawn(part2,'wo')
joinall([p1,p2])#就等于p1.join() 和 p2.join()
print('买单,用餐时间',time.time()-start)
协程实现服务端与客户端通信
链接和通信都是IO密集型操作,我们只需要在这两者之间来回切换其实就能实现并发的效果
服务端检测链接和通信,客户端同时链接服务端
#例子
服务端
from gevent import monkey;monkey.patch_all()
from gevent import spawn
import socket
def server(ip,port,backlog=5)
server=socket.socket()
server.bind((ip,port))
server.listen(backlog)
while True:
conn,addr=server.accept()
print(addr)
spawn(communicate,conn)
def communicate(conn):
while True:
try:
data=conn.recv(1024)
print(data.decode('utf-8'))
conn.send(b'message had get')
except ConnectionResetError:
break
conn.close()
p=spawn(server,'127.0.0.1',8080)
p.join()
客户端
from threading import Thread,current_thread
import socket
def task(n):
client=socket.socket()
client.connect(('127.0.0.1',8080))
while True:
client.send('%s is running from %s'%(n,current_thread().name).encode('utf-8'))
data=client.recv(1024)
print(data)
for i in range(500):
p=Thread(target=task,args=(i,))
IO模型
- 阻塞IO
- 非阻塞IO(服务端通信针对accept用s.setblocking(False)加异常捕获,cpu占用率过高)
- IO多路复用
在只检测一个套接字的情况下,他的效率连阻塞IO都比不上。因为select这个中间人增加了环节。
但是在检测多个套接字的情况下,就能省去wait for data过程
- 异步IO