from multiprocessing import Process,Event
import time
# Event e 有两种状态,一种是False 一种是True.
def processa(e):
print("开始执行process 1 !")
print("修改e的状态")
# 查询 Event的状态,如果是False ,那么则在这里等待,如果状态变成了True,那么则继续执行
e.wait()
print("继续执行")
# 将Event的状态 改为False
e.clear()
def processb(e):
print("开始执行process 2")
time.sleep(2)
print("修改e的状态")
# 修改Event的状态为True,同时另一个线程则继续执行
e.set()
# 查看 Event的状态
print(e.is_set())
time.sleep(2)
print("继续检查状态")
# 获取状态 另一个线程将Event的状态改为了True,程序会在这里阻塞
e.wait()
if __name__ == '__main__':
e = Event()
a = Process(target=processa,args=(e,))
b = Process(target=processb,args=(e,))
a.start()
b.start()
# 限制并发数量
from multiprocessing import Process,Semaphore
import time
def processa(i,s):
# 获取一把锁
s.acquire()
print("这里是%s号"%i)
time.sleep(2)
# 将使用完成的锁放回去,下一个线程获得锁后才可以再次向下执行
s.release()
if __name__ == '__main__':
# 这里是一个计数器,设置最多每次并行执行的进程数量,可以理解为设置了锁的数量
s = Semaphore(4)
for i in range(10):
a = Process(target=processa,args=(i,s))
a.start()
开辟一个池子,在里面放东西,调用进程池里面的任务,进程执行结束还放回来,不存在重复创建重复销毁的过程,提高效率
简单用法
from multiprocessing import Process,Pool
import time
def processa(n):
time.sleep(1)
print(n)
if __name__ == '__main__':
# 此参数根据CPU数量来设定
pool = Pool(4)
# 上面是设置了进程池最大的进程数量 下面的是要执行传的参数,前面放的是函数或类名,后面放的必须是可迭代的,类或者函数必须要有个变量接收这个参数
pool.map(processa,range(100))
进程池和多线程 信号量 执行时间对比
from multiprocessing import Process,Pool,Semaphore
import time
def processa(n):
for i in range(5):
n = n + i
# 由于信号量接收参数不同所以单独一个类
def processb(n,s):
s.acquire()
for i in range(5):
n = n + i
s.release()
if __name__ == '__main__':
# 进程池开始时间
pool_start_time = time.time()
# 设置进程池最大执行数量
pool = Pool(4)
pool.map(processa,range(100))
# 进程池结束时间
pool_stop_time = time.time()
# 计算时间差
pool_time = pool_stop_time - pool_start_time
# 多线程执行
# 多线程开始时间
process_start_time = time.time()
p_list = []
for pr in range(100):
a = Process(target=processa,args=(pr,))
a.start()
p_list.append(a)
[a.join() for a in p_list ]
# 多线程结束时间
process_stop_time = time.time()
# 计算多线程执行时间差
process_time = process_stop_time - process_start_time
# 信号量的方式
semap_start_time = time.time()
s = Semaphore(4)
s_list = []
for se in range(100):
a = Process(target=processb,args=(se,s))
a.start()
s_list.append(a)
[ a.join() for a in s_list ]
semap_stop_time = time.time()
# 计算信号量方式执行后时间差
semap_time = semap_stop_time - semap_start_time
# 输出结果
print("进程池耗时:",pool_time,"多进程耗时",process_time,"信号量耗时",semap_time)
上段代码执行输出结果:
进程池耗时: 0.16051506996154785 多进程耗时 2.6977779865264893 信号量耗时 3.499631404876709
原因:
进程池是创建出四个进程,然后循环执行传递参数,整个执行过程中参与的进程是4个.
信号量和for循环是创建了一百个进程执行,for循环是100个进程同时执行,而信号量则是创建100个进程,每次执行4个,所以时间更久
而for循环创建了100个进程,就要有对应的100个进程的创建和回收过程,所以耗时时间长,进程池只创建了4个进程,省去了创建进程和回收进程的时间\
from multiprocessing import process,Pool
import time
def processa(n):
time.sleep(0.5)
return n*n
if __name__ == '__main__':
pool = Pool(4)
for i in range(10):
# 同步方法: 进程池在此处变成了顺序执行(串行),同时可以接收一个返回值
return_key = pool.apply(processa,args=(i,))
print("返回值:%s"%return_key)
执行之后可以看到依次输出了循环数字的平方,0.5秒一次
from multiprocessing import process,Pool
import time
def processa(n):
time.sleep(0.5)
print(n*n)
if __name__ == '__main__':
pool = Pool(4)
for i in range(10):
return_key = pool.apply_async(processa,args=(i,))
print(return_key)
print("主进程即将结束!")
# 主进程如果直接结束,那么进程池也将结束,所有子进程也就结束了,所以要有一个等待时间
time.sleep(4)
print("主进程执行结束")
执行后,十个任务瞬间创立,主进程执行进入sleep 4,十个任务进入线程池等待可用线程执行,如果主进程没有等待直接结束,那么任务也被直接结束掉
执行后输出的内容,如:
<multiprocessing.pool.ApplyResult object at 0x00000274DF554E20>
这个叫做结果对象.通过结果对象去取运行返回的结果
from multiprocessing import process,Pool
import time
def processa(n):
time.sleep(0.5)
return n * n
if __name__ == '__main__':
pool = Pool(4)
for i in range(10):
return_key = pool.apply_async(processa,args=(i,))
#print(return_key)
proce_return = return_key.get()
print("通过结果对象获取",proce_return)
print("主进程即将结束!")
# 主进程如果直接结束,那么进程池也将结束,所有子进程也就结束了,所以要有一个等待时间
time.sleep(4)
print("主进程执行结束")
执行上段代码可以看到,十个任务依次输出,变成了同步方法.
执行到proce_return = return_key.get() 这段代码时,这段代码会等待上一条执行的返回结果,返回后才会继续执行
优化:不再for循环中获取结果,而是将其加到一个列表中,通过循环列表获取结果
from multiprocessing import process,Pool
import time
def processa(n):
time.sleep(0.5)
return n * n
proce_list = []
if __name__ == '__main__':
pool = Pool(4)
for i in range(10):
return_key = pool.apply_async(processa,args=(i,))
proce_list.append(return_key)
print("开始输出返回结果")
for i in proce_list:
print(i.get())
print("主进程执行结束")
执行后可以看到,输出结果是每次输出4个然后卡顿一会,再次输出.
原因:提交任务是有序的,所以列表中也是有序的,但是执行时是无序的,所以在获取的时候如果有值直接输出,没有则等待.
如果前四个个最先执行,那么输出后等待,如果前四个是最后执行的,那么后面的都执行完毕了,并且有了结果,程序等待后将直接输出.
主进程等待进程池所有进程执行完毕 然后打印结果.
但是查看的时候,进程池可能还会有新的任务,主进程不能一直等待,所以要先冻结进程池,不让任务在交给进程池,同时等待进程池中任务执行完毕查看结果.
扩大时间和进程数:
from multiprocessing import process,Pool
import time
def processa(n):
time.sleep(0.5)
return n
proce_list = []
if __name__ == '__main__':
pool = Pool(5)
for i in range(100):
return_key = pool.apply_async(processa,args=(i,))
proce_list.append(return_key)
# 冻结进程池,不允许接收新任务 不允许接收新任务,但是要等待现有的任务执行完成
pool.close()
# 等待进程池中所有排队任务执行结束
pool.join()
print("开始输出返回结果")
for i in proce_list:
print(i.get())
print("主进程执行结束")
from multiprocessing import Process,Pool
def process1(a):
print("第一个函数 >" ,a)
return a*a
def process2(b):
print("第二个函数 >",b)
if __name__ == '__main__':
pool = Pool()
# callback 将第一个任务的返回值丢给这个变量的任务
return_key = pool.apply_async(process1,args=(3,),callback=process2)
print(return_key.get())
print("结束")
回调函数是在主进程下执行的,PID和主进程PID相同.