如何判断AsyncResult是否永远不会准备好?

时间:2017-01-31 23:37:45

标签: python python-2.7 python-multiprocessing

场景 - 我有一个我正在执行任务的进程池。但是,如果在运行任务时终止了子进程,则AsyncResult对象永远不会被标记为就绪。我希望会发生的事情是它会被标记为准备好并且不成功。

重现这一点:

>>> import multiprocessing
>>> import time
>>> p = multiprocessing.Pool(processes=1)
>>> result = p.apply_async(time.sleep, args=(1000,))
>>> result.ready()
False

在另一个shell中,找到进程ID并将其终止。

>>> result.ready()
False
>>> result.wait(5) # Waits 5 seconds even though subprocess is dead

这是一个问题,因为我有一个线程在等待作业完成,并且通常有一个相当长的超时。如何在不需要等待超时的情况下完成result.wait(timeout)调用?另外,我怎么知道它已被放弃,而不仅仅是任务仍在运行但是我们达到了超时?

2 个答案:

答案 0 :(得分:1)

如果进程意外死亡,Pebble库会通知您。它还支持超时和回调。

这是你的榜样。

from pebble import ProcessPool
from concurrent.futures import TimeoutError

with ProcessPool() as pool:
    future = pool.schedule(time.sleep, args=(1000,), timeout=100)

    try:
        results = future.result()
        print(results)
    except TimeoutError as error:
        print("Function took longer than %d seconds" % error.args[1])
    except ProcessExpired as error:
        print("%s. Exit code: %d" % (error, error.exitcode))
    except Exception as error:
        print("function raised %s" % error)
        print(error.traceback)  # Python's traceback of remote process

documentation中的更多示例。

答案 1 :(得分:1)

呼叫result.wait()将一直等到timeout,除非它从池中收到信号。但是,如果执行kill -9 [pid],则池将立即启动队列中的下一个作业。

因此,它更容易使用,然后手动"民意调查"并检查ready()。您所说的问题是,当作业被杀时,ready()仍为False

要解决此问题,您可以检查pid是否存活。由于ApplyResult没有携带pid,因此您需要其他方法来获取它。你可以这样做:

def test(identifier):
    pid = os.getpid()

    f = open("pids/" + str(pid), "w")
    f.write(str(identifier))
    f.close()

    # do stuff
    time.sleep(1000)

然后创建这样的工作(考虑jobs = [] )。

job = (identifier, pool.apply_async(test, (identifier,)))
jobs.append(job)

标识符不是必需的,但如果您稍后想要确定哪个ApplyResult属于哪个pid,则非常有用。

然后,您可以获得所有工作,并检查每个工作(pid)是否还活着:

def is_alive(pid):
    return os.path.exists("/proc/" + str(pid))

for pid in os.listdir("pids"):
    if is_alive(pid):
        ...
    else:
        ...

考虑每个pid命名文件的内容。然后使用identifier中的jobs,您现在可以链接哪个ApplyResult属于哪个pid,并专门检查哪个作业已死或ready()或者如果以上都不是然后还在跑步。

您还可以创建一个管道并分叉一个子进程。

r, w = os.pipe()

def child():
    global r, w

    data = ...
    time.sleep(100)

    os.close(r)
    w = os.fdopen(w, "w")
    w.write(data)
    w.close()

然后,您只需将数据写回父进程。

def parent(child_pid):
    global r, w

    os.close(w)

    r = os.fdopen(r)
    data = r.read()
    r.close()

    status = os.waitpid(child_pid, 0)
    if status == 0:
        # Everything is fine
    elif status == 9:
        # kill -9 [pid]

    # Process data

然后,您可以使用收到的statusdata来确定子进程发生了什么。

你可以通过这样做来开始。

if __name__ == "__main__":
    child_pid = os.fork()
    if child_pid:
        parent(child_pid)
    else:
        child()

从你的问题我假设是Unix。如果没有随意纠正我。如果任何非Python 2.7潜入答案,那么我道歉。