Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/316.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 使用多处理池的apply\u async方法时,谁运行回调?_Python_Callback_Parallel Processing_Multiprocessing - Fatal编程技术网

Python 使用多处理池的apply\u async方法时,谁运行回调?

Python 使用多处理池的apply\u async方法时,谁运行回调?,python,callback,parallel-processing,multiprocessing,Python,Callback,Parallel Processing,Multiprocessing,我试图了解一下在使用多处理池的apply_sync方法时,幕后发生了什么 谁运行回调方法?它是名为apply\u async的主进程吗 假设我发送了一大堆带有回调的apply\u async命令,然后继续我的程序。当apply\u async开始完成时,我的程序仍在运行。当主进程仍然忙于脚本时,回调如何在“主进程”中运行 这里有一个例子 import multiprocessing import time def callback(x): print '{} running callb

我试图了解一下在使用多处理池的apply_sync方法时,幕后发生了什么

谁运行回调方法?它是名为apply\u async的主进程吗

假设我发送了一大堆带有回调的apply\u async命令,然后继续我的程序。当apply\u async开始完成时,我的程序仍在运行。当主进程仍然忙于脚本时,回调如何在“主进程”中运行

这里有一个例子

import multiprocessing
import time

def callback(x):
    print '{} running callback with arg {}'.format(multiprocessing.current_process().name, x)

def func(x):
    print '{} running func with arg {}'.format(multiprocessing.current_process().name, x)
    return x

pool = multiprocessing.Pool()

args = range(20)

for a in args:
    pool.apply_async(func, (a,), callback=callback)

print '{} going to sleep for a minute'.format(multiprocessing.current_process().name)

t0 = time.time()
while time.time() - t0 < 60:
    pass

print 'Finished with the script'
导入多处理
导入时间
def回调(x):
打印运行带有arg{}的回调的{}。格式(multiprocessing.current_process().name,x)
def func(x):
以arg{}格式打印运行func的“{}”。格式(multiprocessing.current_process().name,x)
返回x
池=多处理。池()
args=范围(20)
对于args中的a:
apply\u async(func,(a,),callback=callback)
打印“{}将要睡眠一分钟”。格式(multiprocessing.current_process().name)
t0=时间。时间()
当time.time()-t0<60时:
通过
打印“已完成脚本”
输出类似于

PoolWorker-1正在运行参数为0的func

PoolWorker-2正在运行参数为1的func

PoolWorker-3正在运行参数为2的func


MainProcess将要睡眠一分钟文档中确实有一个提示:

回调应该立即完成,因为否则 处理结果将被阻止

回调在主进程中处理,但它们在各自的独立线程中运行。当您创建
池时
实际上会在内部创建一些
线程
对象:

class Pool(object):
    Process = Process

    def __init__(self, processes=None, initializer=None, initargs=(),
                 maxtasksperchild=None):
        self._setup_queues()
        self._taskqueue = Queue.Queue()
        self._cache = {}
        ... # stuff we don't care about
        self._worker_handler = threading.Thread(
            target=Pool._handle_workers,
            args=(self, )
            )
        self._worker_handler.daemon = True
        self._worker_handler._state = RUN 
        self._worker_handler.start()

        self._task_handler = threading.Thread(
            target=Pool._handle_tasks,
            args=(self._taskqueue, self._quick_put, self._outqueue,
                  self._pool, self._cache)
            )
        self._task_handler.daemon = True
        self._task_handler._state = RUN 
        self._task_handler.start()

        self._result_handler = threading.Thread(
            target=Pool._handle_results,
            args=(self._outqueue, self._quick_get, self._cache)
            )
        self._result_handler.daemon = True
        self._result_handler._state = RUN
        self._result_handler.start()
我们感兴趣的线程是
\u result\u handler
;我们很快就会知道原因

切换一秒钟,当您运行
apply\u async
时,它会在内部创建一个
ApplyResult
对象来管理从子对象获取结果:

def apply_async(self, func, args=(), kwds={}, callback=None):
    assert self._state == RUN
    result = ApplyResult(self._cache, callback)
    self._taskqueue.put(([(result._job, None, func, args, kwds)], None))
    return result

class ApplyResult(object):

    def __init__(self, cache, callback):
        self._cond = threading.Condition(threading.Lock())
        self._job = job_counter.next()
        self._cache = cache
        self._ready = False
        self._callback = callback
        cache[self._job] = self


    def _set(self, i, obj):
        self._success, self._value = obj
        if self._callback and self._success:
            self._callback(self._value)
        self._cond.acquire()
        try:
            self._ready = True
            self._cond.notify()
        finally:
            self._cond.release()
        del self._cache[self._job]
如您所见,
\u set
方法是一个最终实际执行传入的
回调
的方法,假设任务成功。还请注意,它将自己添加到
\uuu init\uuu
末尾的全局
缓存中

现在,回到
\u result\u处理程序
线程对象。该对象调用
\u handle\u results
函数,如下所示:

    while 1:
        try:
            task = get()
        except (IOError, EOFError):
            debug('result handler got EOFError/IOError -- exiting')
            return

        if thread._state:
            assert thread._state == TERMINATE
            debug('result handler found thread._state=TERMINATE')
            break

        if task is None:
            debug('result handler got sentinel')
            break

        job, i, obj = task
        try:
            cache[job]._set(i, obj)  # Here is _set (and therefore our callback) being called!
        except KeyError:
            pass

        # More stuff

这是一个循环,它只是从队列中提取子对象的结果,在
缓存中找到它的条目,然后调用
\u set
,执行回调。即使您处于循环中,它也可以运行,因为它不在主线程中运行。

感谢Dano花时间编写如此详细的响应!如果我理解正确,池将创建一个新线程(result\u处理程序),其任务是等待apply\u async完成,然后调用result\u处理程序线程中的回调(这是主进程的一部分)。回调(针对单个池对象)是否会按顺序调用?也就是说,一组apply_async可能会一起完成,但结果处理程序将逐个串行运行回调?还有一个问题。如果回调函数和主脚本都与相同的对象(在MainProcess中)发生冲突怎么办?会有不可预测的行为吗?也就是说,如果回调和主脚本后面的内容都试图写入相同的文件或修改相同的数组。当回调实际运行时,谁知道主脚本当时将执行什么。@Alex是的,回调将按顺序执行。
\u result\u handler
线程从队列中取出一个已完成的任务,调用
\u set
(它运行回调),然后转到下一个任务。这就是为什么文档中说要确保回调立即完成;执行回调会阻止处理其他结果。@Alex您确实需要担心在回调中更改的任何对象的线程安全性。一般来说,我建议在回调中尽可能少地执行操作,但是如果您确实需要接触共享状态,则必须使用某种互斥来保护它。