这里假设有一个列表,其中存储100个数据,需提取列表中的内容使用多线程进行处理。假设处理每个数据需要花费0.3秒,但进程的话需要处理33秒。
multiprocessing的apply()方法并不是异步的,需要使用apply_async才行,它们之间的区别和介绍:
apply():
非异步(子进程不是同时执行的),堵塞主进程。
它的非异步体现在:一个一个按顺序执行子进程, 子进程不是同时执行的。
它的堵塞体现在:等到全部子进程都执行完毕后,继续执行apply()后面主进程的代码。
apply_async():
异步的,不堵塞主进程。
它的异步体现在:子进程之间是同时执行的。子进程被分配到不同的cpu上被执行。
它的非堵塞体现在:他不会等待子进程完全执行完毕, 主进程会继续执行, 他会根据系统调度来进行进程之间的切换。如果想堵塞主要进程,需要用.join()函数来堵塞主进程。
使用 pool.apply_async方法来使用多进程处理数据,需要注意的问题是,列表中的数据只需要被处理一次即可,重复的取用会花费额外的时间
我先实验了两种数据处理方法
1.使用manager().list()来同时存储需要处理的数据和处理后的结果
2.使用manager().list()存储需要处理的数据,处理的结果在每个进程运行完后使用manager().list()统一汇总
import multiprocessing as mp
import time
def myfun(rawlist=[], resultlist=[]):
while rawlist:
temp = rawlist.pop(0)
resultlist.append(str(temp))
time.sleep(0.3)
def myfun2(rawlist=[], final_result=[]):
resultlist = []
while rawlist:
temp = rawlist.pop(0)
resultlist.append(str(temp))
time.sleep(0.3)
final_result.append(resultlist)
if __name__ == '__main__':
pool = mp.Pool(processes=4)
start_time = time.time()
manager = mp.Manager
rawlist = manager().list()
resultlist = manager().list()
final_result = manager().list()
for i in range(100):
rawlist.append(i)
for i in range(4):
pool.apply_async(myfun, (rawlist, resultlist,))
pool.close()
pool.join()
print(time.time() - start_time)
print(resultlist)
pool = mp.Pool(processes=4)
start_time = time.time()
manager = mp.Manager
rawlist = manager().list()
resultlist = manager().list()
final_result = manager().list()
start_time = time.time()
for i in range(100):
rawlist.append(i)
for i in range(4):
pool.apply_async(myfun2, (rawlist, final_result,))
pool.close()
pool.join()
print(time.time() - start_time)
print(final_result)
其中运行结果如下:
9.088896989822388
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '54', '53', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '66', '65', '67', '68', '69', '71', '70', '72', '74', '75', '73', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99']
7.957397699356079
[['1', '5', '9', '13', '16', '22', '24', '28', '33', '36', '41', '44', '48', '52', '56', '60', '67', '68', '75', '77', '83', '86', '90', '93', '99'], ['2', '4', '10', '12', '19', '23', '27', '31', '34', '38', '40', '46', '51', '55', '58', '61', '64', '70', '74', '79', '82', '85', '88', '95', '96'], ['0', '7', '8', '15', '17', '20', '25', '30', '32', '37', '43', '45', '49', '53', '57', '62', '65', '71', '73', '76', '80', '87', '91', '94', '98'], ['3', '6', '11', '14', '18', '21', '26', '29', '35', '39', '42', '47', '50', '54', '59', '63', '66', '69', '72', '78', '81', '84', '89', '92', '97']]
可以看到,方法1用的时间更长,但是能在使用了多进程的情况下保证数据的顺序取用,方法2用时更短,但是不能保证数据的顺序处理。后面又想到一种方法,就是先对数据进行切片,然后把分片的数据交给多进程运行,再使用manager().list()统一汇总,
processnum=4
pool = mp.Pool(processes=processnum)
start_time = time.time()
manager = mp.Manager
rawlist = []
resultlist = manager().list()
final_result = manager().list()
start_time = time.time()
for i in range(100):
rawlist.append(i)
temp=[]
step=len(rawlist)//processnum
for i in range(0,processnum-1):
temp.append(rawlist[step*i:step*i+step])
temp.append(rawlist[step*(processnum-1):])
print(temp)
for i in range(4):
pool.apply_async(myfun2, (temp[i], final_result,))
pool.close()
pool.join()
print(time.time() - start_time)
print(final_result)
最后运行的结果如下,可以看到这么做速度更快,虽然结果不是顺序的,但其中的元素依然是保持有序的,如果更改数据结果或者添加一些排序字段可以尝试恢复到之前有序的结果
7.8769989013671875
[['75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99'], ['25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49'], ['50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74'], ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24']]