【发布时间】:2016-12-15 21:24:01
【问题描述】:
我一直在尝试为控制某些硬件的库编写交互式包装器(用于 ipython)。一些调用在 IO 上很重,因此并行执行任务是有意义的。使用线程池(几乎)效果很好:
from multiprocessing.pool import ThreadPool
class hardware():
def __init__(IPaddress):
connect_to_hardware(IPaddress)
def some_long_task_to_hardware(wtime):
wait(wtime)
result = 'blah'
return result
pool = ThreadPool(processes=4)
Threads=[]
h=[hardware(IP1),hardware(IP2),hardware(IP3),hardware(IP4)]
for tt in range(4):
task=pool.apply_async(h[tt].some_long_task_to_hardware,(1000))
threads.append(task)
alive = [True]*4
Try:
while any(alive) :
for tt in range(4): alive[tt] = not threads[tt].ready()
do_other_stuff_for_a_bit()
except:
#some command I cannot find that will stop the threads...
raise
for tt in range(4): print(threads[tt].get())
如果用户想要停止进程或者do_other_stuff_for_a_bit() 中存在 IO 错误,就会出现问题。按 Ctrl+C 会停止主进程,但工作线程会继续运行,直到当前任务完成。
有什么方法可以停止这些线程而不必重写库或让用户退出 python?我在其他示例中看到的pool.terminate() 和pool.join() 似乎无法完成这项工作。
实际的例程(而不是上面的简化版本)使用日志记录,尽管所有工作线程都在某个时候关闭,但我可以看到他们开始运行的进程一直持续到完成(作为硬件,我可以看到他们的透过房间看的效果)。
这是在 python 2.7 中。
更新:
解决方案似乎是改用 multiprocessing.Process 而不是线程池。我尝试的测试代码是运行 foo_pulse:
class foo(object):
def foo_pulse(self,nPulse,name): #just one method of *many*
print('starting pulse for '+name)
result=[]
for ii in range(nPulse):
print('on for '+name)
time.sleep(2)
print('off for '+name)
time.sleep(2)
result.append(ii)
return result,name
如果您尝试使用 ThreadPool 运行它,那么 ctrl-C 不会阻止 foo_pulse 运行(即使它会立即终止线程,打印语句也会继续出现:
from multiprocessing.pool import ThreadPool
import time
def test(nPulse):
a=foo()
pool=ThreadPool(processes=4)
threads=[]
for rn in range(4) :
r=pool.apply_async(a.foo_pulse,(nPulse,'loop '+str(rn)))
threads.append(r)
alive=[True]*4
try:
while any(alive) : #wait until all threads complete
for rn in range(4):
alive[rn] = not threads[rn].ready()
time.sleep(1)
except : #stop threads if user presses ctrl-c
print('trying to stop threads')
pool.terminate()
print('stopped threads') # this line prints but output from foo_pulse carried on.
raise
else :
for t in threads : print(t.get())
但是使用 multiprocessing.Process 的版本按预期工作:
import multiprocessing as mp
import time
def test_pro(nPulse):
pros=[]
ans=[]
a=foo()
for rn in range(4) :
q=mp.Queue()
ans.append(q)
r=mp.Process(target=wrapper,args=(a,"foo_pulse",q),kwargs={'args':(nPulse,'loop '+str(rn))})
r.start()
pros.append(r)
try:
for p in pros : p.join()
print('all done')
except : #stop threads if user stops findRes
print('trying to stop threads')
for p in pros : p.terminate()
print('stopped threads')
else :
print('output here')
for q in ans :
print(q.get())
print('exit time')
我已经为库 foo 定义了一个包装器(因此它不需要重新编写)。如果不需要返回值,则此包装器也不是:
def wrapper(a,target,q,args=(),kwargs={}):
'''Used when return value is wanted'''
q.put(getattr(a,target)(*args,**kwargs))
从文档中我看不出为什么池不起作用(除了错误)。
【问题讨论】:
-
你有任何理由使用无证类吗?使用
concurrent.futures模块可能会更好。 -
没有真正的理由使用未记录的类 - 除了这是我在研究如何做时遇到的示例代码中使用的内容。
-
@SuperSaiyan:它以不同的名称记录;
ThreadPool在multiprocessing.dummy.Pool下以有记录的方式公开,其中multiprocessing.dummyis a close copy of themultiprocessingAPI backed by threads instead of processes。
标签: python threadpool