web-dev-qa-db-ja.com

タイムアウト後にmultiprocessing.Poolでタスクを中止するにはどうすればよいですか?

pythonのマルチプロセッシングパッケージをこのように使用しようとしています:

featureClass = [[1000,k,1] for k in drange(start,end,step)] #list of arguments
for f in featureClass:
  pool .apply_async(worker, args=f,callback=collectMyResult)
pool.close()
pool.join

プールのプロセスから、その結果を返すのに60秒以上かかるプロセスを待たないようにしたいと思います。それは可能ですか?

16
farhawa

これは、worker関数を変更せずにこれを実行できる方法です。次の2つの手順が必要です。

  1. maxtasksperchildオプションを使用してmultiprocessing.Poolに渡すことができるため、タスクが実行されるたびにプール内のワーカープロセスが確実に再起動されます。
  2. 既存のワーカー関数を別の関数でラップします。この関数は、デーモンスレッドでworkerを呼び出し、そのスレッドからの結果をtimeout秒間待機します。プロセスはデーモンスレッドの終了を待たずに終了するため、デーモンスレッドの使用は重要です。

タイムアウトの期限が切れた場合は、ラッパー関数を終了(または中止-自分次第)し、タスクを終了します。maxtasksperchild=1を設定したため、Poolによってワーカーが終了しますプロセスし、新しいものを開始します。これは、実際の作業を行っているバックグラウンドスレッドもデーモンスレッドであり、そのスレッドが実行しているプロセスがシャットダウンされたために中止されることを意味します。

import multiprocessing
from multiprocessing.dummy import Pool as ThreadPool
from functools import partial

def worker(x, y, z):
    pass # Do whatever here

def collectMyResult(result):
    print("Got result {}".format(result))

def abortable_worker(func, *args, **kwargs):
    timeout = kwargs.get('timeout', None)
    p = ThreadPool(1)
    res = p.apply_async(func, args=args)
    try:
        out = res.get(timeout)  # Wait timeout seconds for func to complete.
        return out
    except multiprocessing.TimeoutError:
        print("Aborting due to timeout")
        raise

if __name__ == "__main__":
    pool = multiprocessing.Pool(maxtasksperchild=1)
    featureClass = [[1000,k,1] for k in range(start,end,step)] #list of arguments
    for f in featureClass:
      abortable_func = partial(abortable_worker, worker, timeout=3)
      pool.apply_async(abortable_func, args=f,callback=collectMyResult)
    pool.close()
    pool.join()

タイムアウトする関数はmultiprocessing.TimeoutErrorを発生させます。これは、タイムアウトが発生したときにコールバックが実行されないことを意味します。これが許容できない場合は、exceptを呼び出すのではなく、abortable_workerraiseブロックを変更して何かを返すだけです。

また、すべてのタスク実行後にワーカープロセスを再起動すると、オーバーヘッドが増加するため、Poolのパフォーマンスに悪影響を及ぼすことにも注意してください。あなたはあなたのユースケースのためにそれを測定し、トレードオフが仕事を中止する能力を持つことの価値があるかどうかを見るべきです。それが問題である場合、外部から殺すのではなく、実行時間が長すぎる場合はworkerを協調的に中断するなど、別のアプローチを試す必要があるかもしれません。このトピックをカバーするSOに関する質問がたくさんあります。

29
dano

gevent.Timeoutを使用して、実行中のワーカーの時間を設定できます。 geventチュートリアル

from multiprocessing.dummy import Pool 
#you should install gevent.
from gevent import Timeout
from gevent import monkey
monkey.patch_all()
import time

def worker(sleep_time):
    try:

        seconds = 5  # max time the worker may run
        timeout = Timeout(seconds) 
        timeout.start()
        time.sleep(sleep_time)
        print "%s is a early bird"%sleep_time
    except:
        print "%s is late(time out)"%sleep_time

pool = Pool(4)

pool.map(worker, range(10))


output:
0 is a early bird
1 is a early bird
2 is a early bird
3 is a early bird
4 is a early bird
8 is late(time out)
5 is late(time out)
6 is late(time out)
7 is late(time out)
9 is late(time out)
3
thinkdeeper