关于python多进程的创建方式请先看入门python多进程(一)
3.进程锁:
由于进程之间的数据不共享,有时候多个进程需要同时访问同一个文件,这就会引发数据安全或顺序混乱问题。
这种情况下,可以使用进程锁,加锁的目的是为了保证多个进程修改同一块数据时,同一时间只能有一个修改,即串行的修改。这种方式会减慢速度,但是牺牲了速度而保证了数据安全。
举一个最常见的买票问题。ticket.json里写的{“count”: 4},表示现在只有4张票,我会启动5个进程去买票,这就意味着第5个进程会买票失败
import os
import time
import json
import random
import multiprocessing
from multiprocessing import Lock
def buy_ticket(file_name, lock):
#进程锁开启,同一时刻只允许一个进程买票操作
lock.acquire()
with open(file_name, 'r', encoding='utf-8') as f:
dic = json.loads(f.read())
if dic["count"] > 0:
print("{}查询,还剩{}张票" .format(os.getpid(),dic["count"]))
dic["count"] -= 1
time.sleep(random.randint(1,3))
with open(file_name, 'w', encoding='utf-8') as f:
f.write(json.dumps(dic))
print("{}购票成功".format(os.getpid()))
else:
print("{}购票失败" .format (os.getpid()))
#记得进程锁释放
lock.release()
if __name__ == "__main__":
lock = Lock()
for i in range(5):
sub_process = multiprocessing.Process(target=buy_ticket, args=("ticket.json", lock) )
sub_process.start()
运行结果:
4.进程池:
对于操作对象数目不大的时候,可以直接利用multiprocessing中的Process动态成生多个进程。但是当被操作对象数目很大的时候,开启很大数量的进程反而会降低效率。因此,这时候可以使用进程池去管理一定数量的进程,避免太过繁琐的手动去限制进程数量,此时可以发挥进程池的功效。
Pool可以提供指定数量的进程供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。
A.阻塞(同步)方式
import os
import time
from multiprocessing import Pool
def work(n):
print("{} statrt".format(os.getpid()))
time.sleep(1)
print("{} end".format(os.getpid()))
return n * 2
if __name__ == "__main__":
pool = Pool(processes=3) #定义线程个数
res_l = []
for i in range(6):
res = pool.apply(work, (i, ))#apply是阻塞方式
res_l.append(res)#直接获得结果
pool.close()#关闭pool,使其不在接受新的任务
pool.join()#主进程阻塞,子进程结束,主进程才可以结束
print(res_l)
运行结果:可以看出9个任务,重复使用3个进程进行处理。此外,阻塞的方式是某个进程跑完之后,下一个进程才会开启。
2648 statrt
2648 end
17160 statrt
17160 end
9616 statrt
9616 end
2648 statrt
2648 end
17160 statrt
17160 end
9616 statrt
9616 end
[0, 2, 4, 6, 8, 10]
B.非阻塞(异步)方式
import os
import time
from multiprocessing import Pool
def work(n):
print("{} statrt".format(os.getpid()))
time.sleep(1)
print("{} end".format(os.getpid()))
return n * 2
if __name__ == "__main__":
pool = Pool(processes=3) #定义线程个数
res_l = []
for i in range(6):
res = pool.apply_async(work, (i, ))#非阻塞方式
res_l.append(res)#非直接获得结果
pool.close()#关闭pool,使其不在接受新的任务
pool.join()#主进程阻塞,子进程结束,主进程才可以结束
print([res.get()for res in res_l])#get方式获取结果
运行结果:可以看出进程是异步进行的,此外,进程的结果不可以直接获得,而是要通过.get()方法获得
10292 statrt
8292 statrt
14144 statrt
10292 end
10292 statrt
8292 end
14144 end
8292 statrt
14144 statrt
10292 end
8292 end
14144 end
[0, 2, 4, 6, 8, 10]