57

新しいconcurrent.futuresモジュール (Python 2 バックポートも備えている) を使用して、単純なマルチスレッド I/O を実行しています。このモジュールを使用して開始されたタスクを完全に強制終了する方法がわかりません。

私が見ている動作を再現する次の Python 2/3 スクリプトを確認してください。

#!/usr/bin/env python
from __future__ import print_function

import concurrent.futures
import time


def control_c_this():
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        future1 = executor.submit(wait_a_bit, name="Jack")
        future2 = executor.submit(wait_a_bit, name="Jill")

        for future in concurrent.futures.as_completed([future1, future2]):
            future.result()

        print("All done!")


def wait_a_bit(name):
    print("{n} is waiting...".format(n=name))
    time.sleep(100)


if __name__ == "__main__":
    control_c_this()

このスクリプトの実行中は、通常の Control-C キーボード割り込みを使用して完全に強制終了することは不可能に見えます。私はOS Xで実行しています。

  • Python 2.7 ではkill、コマンド ラインからスクリプトを強制終了する必要があります。Control-C は単に無視されます。
  • Python 3.4 では、Control-C を 2 回押すと機能しますが、多くの奇妙なスタック トレースがダンプされます。

私がオンラインで見つけたほとんどのドキュメントは、古いthreadingモジュールでスレッドをきれいに殺す方法について語っています。ここではどれも当てはまらないようです。

concurrent.futuresまた、モジュール内で提供されているもの (Executor.shutdown()や など) を停止するすべてのメソッドはFuture.cancel()、Future がまだ開始されていないか完了している場合にのみ機能しますが、この場合は無意味です。すぐに未来を中断したい。

私の使用例は単純です。ユーザーが Control-C を押すと、適切に動作するスクリプトと同様に、スクリプトはすぐに終了する必要があります。それだけです。

では、使用時にこの動作を取得する適切な方法は何concurrent.futuresですか?

4

3 に答える 3

38

なんか痛いです。基本的に、メイン スレッドが終了する前にワーカー スレッドを終了する必要があります。彼らがそうしない限り、あなたは出ることはできません。典型的な回避策は、各スレッドがチェックして、さらに作業を行う必要があるかどうかを判断できるグローバル状態を設定することです。

その理由を説明する引用を次に示します。本質的に、インタープリターが終了したときにスレッドが終了すると、悪いことが起こる可能性があります。

これが実際の例です。子スレッドのスリープ期間のため、Cc の伝播には最大で 1 秒かかることに注意してください。

#!/usr/bin/env python
from __future__ import print_function

import concurrent.futures
import time
import sys

quit = False
def wait_a_bit(name):
    while not quit:
        print("{n} is doing work...".format(n=name))
        time.sleep(1)

def setup():
    executor = concurrent.futures.ThreadPoolExecutor(max_workers=5)
    future1 = executor.submit(wait_a_bit, "Jack")
    future2 = executor.submit(wait_a_bit, "Jill")

    # main thread must be doing "work" to be able to catch a Ctrl+C 
    # http://www.luke.maurits.id.au/blog/post/threads-and-signals-in-python.html
    while (not (future1.done() and future2.done())):
        time.sleep(1)

if __name__ == "__main__":
    try:
        setup()
    except KeyboardInterrupt:
        quit = True
于 2015-03-24T15:58:33.417 に答える