Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/355.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中停止线程池中的进程_Python_Threadpool - Fatal编程技术网

在Python中停止线程池中的进程

在Python中停止线程池中的进程,python,threadpool,Python,Threadpool,我一直在尝试为控制某些硬件的库编写一个交互式包装器(用于ipython)。有些调用在IO上很繁重,因此并行执行任务是有意义的。使用线程池(几乎)效果很好: from multiprocessing.pool import ThreadPool class hardware(): def __init__(IPaddress): connect_to_hardware(IPaddress) def some_long_task_to_hardware(wtime

我一直在尝试为控制某些硬件的库编写一个交互式包装器(用于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\u other\u stuff\u for a\u bit()
中出现IO错误,则会出现问题。按Ctrl+C键可停止主进程,但辅助线程将继续运行,直到其当前任务完成。
是否有某种方法可以停止这些线程,而不必重写库或让用户退出python<代码>池.terminate()和
池.join()
这两个我在其他示例中看到的方法似乎都不起作用

实际的例程(而不是上面的简化版本)使用日志记录,尽管所有工作线程在某个时候都被关闭,但我可以看到它们开始运行的进程一直持续到完成(作为硬件,我可以通过查看整个房间来看到它们的效果)

这是在Python2.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))

从文档中我看不出池不能工作的原因(除了bug)。

这是一个非常有趣的并行使用

但是,如果使用的是
多处理
,则目标是让多个进程并行运行,而不是一个进程运行多个线程

考虑以下几个更改,以使用
多处理实现它:

您有这些并行运行的功能:

import time
import multiprocessing as mp


def some_long_task_from_library(wtime):
    time.sleep(wtime)


class MyException(Exception): pass

def do_other_stuff_for_a_bit():
    time.sleep(5)
    raise MyException("Something Happened...")
让我们创建并启动流程,例如4:

procs = []  # this is not a Pool, it is just a way to handle the
            # processes instead of calling them p1, p2, p3, p4...
for _ in range(4):
    p = mp.Process(target=some_long_task_from_library, args=(1000,))
    p.start()
    procs.append(p)
mp.active_children()   # this joins all the started processes, and runs them.
这些进程是并行运行的,可能是在一个单独的cpu内核中,但这要由操作系统来决定。您可以检查您的系统监视器

同时,您运行一个将中断的进程,并且您希望停止正在运行的进程,而不是将其孤立:

try:
    do_other_stuff_for_a_bit()
except MyException as exc:
    print(exc)
    print("Now stopping all processes...")
    for p in procs:
        p.terminate()
print("The rest of the process will continue")
如果在一个或所有子进程终止时继续主进程没有意义,则应处理主程序的退出


希望它有帮助,您可以将其中的一些内容改编为您的库。

在回答池为什么不工作的问题时,这是因为(如中所引用的),然后main需要由子进程导入,并且由于此项目的性质,正在使用交互式python

与此同时,还不清楚为什么ThreadPool会这样做,尽管名称中有线索。ThreadPool使用multiprocessing.dummy创建其工作进程池,正如所述,multiprocessing.dummy只是线程模块的包装器。池使用multiprocessing.Process。这可以通过此测试看出:

p=ThreadPool(processes=3)
p._pool[0]
<DummyProcess(Thread23, started daemon 12345)> #no terminate() method

p=Pool(processes=3)
p._pool[0]
<Process(PoolWorker-1, started daemon)> #has handy terminate() method if needed
对象实例内属性的更改不会传递回主程序。例如,上面的类foo也可以有如下方法: def addIP(新IP): self.hardwareIP=newIP 调用
r=mp.Process(target=A.addIP,args=(127.0.0.1))
不会更新
A


对于复杂对象来说,唯一的解决方法似乎是使用自定义
管理器
共享内存,该管理器可以访问基于库的非常大的复杂对象的对象
a
的方法和属性。最好使用
dir(foo)
填充管理器。如果我能想出如何用一个例子更新这个答案(为了我将来的自己和其他人)。

如果出于某些原因使用线程更可取,我们可以使用

我们可以向要终止的线程发送一些信号。最简单的信号是全局变量:


你有任何理由使用未记录的类吗?你可能会更幸运地使用
concurrent.futures
模块。没有真正的理由使用未记录的类-除了这是我在研究如何使用它时遇到的示例代码中使用的。@SuperSaiyan:它是以不同的名称记录的;
ThreadPool
multiprocessing.dummy.Pool
下有一个文档化的方式公开,其中。在我的例子中,如果所有的东西都在同一个CPU上运行,那么并行运行的原因是IO上有大量的等待。然而,这种方法的一个缺点是很难从调用返回值。现在我解决了这个问题使用包装器函数实现此功能-请参阅我更新的帖子。根据需要从调用返回的值的类型,您可以使用
队列
管道
、共享内存
数组
,甚至磁盘文件。在某些情况下,您可能需要使用
锁定
s。
def wrapper(a,target,q,args=(),kwargs={}):
    '''Used when return value is wanted'''
    q.put(getattr(a,target)(*args,**kwargs))
import time
from multiprocessing.pool import ThreadPool

_FINISH = False

def hang():
    while True:
        if _FINISH:
            break
        print 'hanging..'
        time.sleep(10)


def main():
    global _FINISH
    pool = ThreadPool(processes=1)
    pool.apply_async(hang)
    time.sleep(10)
    _FINISH = True
    pool.terminate()
    pool.join()
    print 'main process exiting..'


if __name__ == '__main__':
    main()