我有以下代码利用多处理迭代大型列表并找到匹配项。一旦在任何一个进程中找到匹配,我如何才能停止所有进程?我见过一些例子,但我似乎都不符合我在这里做的事情。
#!/usr/bin/env python3.5
import sys, itertools, multiprocessing, functools
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12234567890!@#$%^&*?,()-=+[]/;"
num_parts = 4
part_size = len(alphabet) // num_parts
def do_job(first_bits):
for x in itertools.product(first_bits, *itertools.repeat(alphabet, num_parts-1)):
# CHECK FOR MATCH HERE
print(''.join(x))
# EXIT ALL PROCESSES IF MATCH FOUND
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=4)
results = []
for i in range(num_parts):
if i == num_parts - 1:
first_bit = alphabet[part_size * i :]
else:
first_bit = alphabet[part_size * i : part_size * (i+1)]
pool.apply_async(do_job, (first_bit,))
pool.close()
pool.join()
感谢您的时间。
更新1:
我已经实现了@ShadowRanger在伟大方法中建议的更改,它几乎按照我想要的方式工作。所以我添加了一些日志记录来指示进度,并进行了测试'钥匙在那里匹配。 我希望能够独立于num_parts增加/减少iNumberOfProcessors。在这个阶段,当我将它们都放在4时,一切都按预期工作,4个进程旋转(一个额外的控制台)。当我更改iNumberOfProcessors = 6时,6个进程会启动,但只有它们有任何CPU使用率。所以看来2是空闲的。在上面的解决方案中,我能够在不增加num_parts的情况下设置更高的内核数量,并且所有进程都将被使用。
我不确定如何重构这种新方法以给我相同的功能。你能看看并给我一些方向,重构需要能够彼此独立地设置iNumberOfProcessors和num_parts并仍然使用所有进程吗?
以下是更新后的代码:
#!/usr/bin/env python3.5
import sys, itertools, multiprocessing, functools
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12234567890!@#$%^&*?,()-=+[]/;"
num_parts = 4
part_size = len(alphabet) // num_parts
iProgressInterval = 10000
iNumberOfProcessors = 6
def do_job(first_bits):
iAttemptNumber = 0
iLastProgressUpdate = 0
for x in itertools.product(first_bits, *itertools.repeat(alphabet, num_parts-1)):
sKey = ''.join(x)
iAttemptNumber = iAttemptNumber + 1
if iLastProgressUpdate + iProgressInterval <= iAttemptNumber:
iLastProgressUpdate = iLastProgressUpdate + iProgressInterval
print("Attempt#:", iAttemptNumber, "Key:", sKey)
if sKey == 'test':
print("KEY FOUND!! Attempt#:", iAttemptNumber, "Key:", sKey)
return True
def get_part(i):
if i == num_parts - 1:
first_bit = alphabet[part_size * i :]
else:
first_bit = alphabet[part_size * i : part_size * (i+1)]
return first_bit
if __name__ == '__main__':
# with statement with Py3 multiprocessing.Pool terminates when block exits
with multiprocessing.Pool(processes = iNumberOfProcessors) as pool:
# Don't need special case for final block; slices can
for gotmatch in pool.imap_unordered(do_job, map(get_part, range(num_parts))):
if gotmatch:
break
else:
print("No matches found")
更新2:
好的,这是我试图尝试@noxdafox的建议。我根据他提出的建议链接汇总了以下内容。不幸的是,当我运行它时,我收到错误:
...第322行,在apply_async中 引发ValueError(&#34;池未运行&#34;) ValueError:池未运行
任何人都可以就如何使其发挥作用给我一些指导。
基本上问题是我的第一次尝试进行了多处理,但是一旦找到匹配就不支持取消所有进程。
我的第二次尝试(基于@ShadowRanger建议)解决了这个问题,但打破了能够独立扩展进程数和num_parts大小的功能,这是我的第一次尝试。
我的第三次尝试(基于@noxdafox建议)引发了上述错误。
如果有人可以就如何维护我的第一次尝试的功能(能够独立扩展进程数和num_parts大小)给我一些指导,并添加一旦找到匹配就取消所有进程的功能非常感谢。
感谢您的时间。
以下是基于@noxdafox建议的第三次尝试的代码:
#!/usr/bin/env python3.5
import sys, itertools, multiprocessing, functools
alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12234567890!@#$%^&*?,()-=+[]/;"
num_parts = 4
part_size = len(alphabet) // num_parts
iProgressInterval = 10000
iNumberOfProcessors = 4
def find_match(first_bits):
iAttemptNumber = 0
iLastProgressUpdate = 0
for x in itertools.product(first_bits, *itertools.repeat(alphabet, num_parts-1)):
sKey = ''.join(x)
iAttemptNumber = iAttemptNumber + 1
if iLastProgressUpdate + iProgressInterval <= iAttemptNumber:
iLastProgressUpdate = iLastProgressUpdate + iProgressInterval
print("Attempt#:", iAttemptNumber, "Key:", sKey)
if sKey == 'test':
print("KEY FOUND!! Attempt#:", iAttemptNumber, "Key:", sKey)
return True
def get_part(i):
if i == num_parts - 1:
first_bit = alphabet[part_size * i :]
else:
first_bit = alphabet[part_size * i : part_size * (i+1)]
return first_bit
def grouper(iterable, n, fillvalue=None):
args = [iter(iterable)] * n
return itertools.zip_longest(*args, fillvalue=fillvalue)
class Worker():
def __init__(self, workers):
self.workers = workers
def callback(self, result):
if result:
self.pool.terminate()
def do_job(self):
print(self.workers)
pool = multiprocessing.Pool(processes=self.workers)
for part in grouper(alphabet, part_size):
pool.apply_async(do_job, (part,), callback=self.callback)
pool.close()
pool.join()
print("All Jobs Queued")
if __name__ == '__main__':
w = Worker(4)
w.do_job()
答案 0 :(得分:1)
您可以查看this question以查看解决问题的实施示例。
这也适用于concurrent.futures池。
只需将map
方法替换为apply_async
,然后在调用者的列表中进行迭代。
像这样。
for part in grouper(alphabet, part_size):
pool.apply_async(do_job, part, callback=self.callback)
答案 1 :(得分:0)
multiprocessing
并非真正用于取消任务,但是您可以使用pool.imap_unordered
为您的特定情况模拟它,并在您获得匹配时终止该池:
def do_job(first_bits):
for x in itertools.product(first_bits, *itertools.repeat(alphabet, num_parts-1)):
# CHECK FOR MATCH HERE
print(''.join(x))
if match:
return True
# If we exit loop without a match, function implicitly returns falsy None for us
# Factor out part getting to simplify imap_unordered use
def get_part(i):
if i == num_parts - 1:
first_bit = alphabet[part_size * i :]
else:
first_bit = alphabet[part_size * i : part_size * (i+1)]
if __name__ == '__main__':
# with statement with Py3 multiprocessing.Pool terminates when block exits
with multiprocessing.Pool(processes=4) as pool:
# Don't need special case for final block; slices can
for gotmatch in pool.imap_unordered(do_job, map(get_part, range(num_parts))):
if gotmatch:
break
else:
print("No matches found")
这将为每个部分运行do_job
,以尽可能快的速度返回结果。当工作人员返回True
时,循环中断,with
的{{1}}语句退出,Pool
- terminate
(放弃所有工作)进度)。
请注意,虽然这有效,但它有点滥用Pool
;它不会在不终止整个multiprocessing
的情况下处理取消单个任务。如果您需要更细粒度的任务取消,您将要查看concurrent.futures
,但即使在那里,它也只能取消未分配的任务;一旦它们正在运行,它们就不能在不终止Pool
或使用边带终止方法的情况下被取消(让任务轮询一些进程间对象间歇性地确定它是否应该继续运行)