web-dev-qa-db-ja.com

フューチャーが始まったらどうやって殺すのですか?

新しい _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で実行しています。

  • On Python 2.7私はスクリプトを殺すためにコマンドラインからkillに頼らなければなりません。Control-Cは無視されます。
  • Python 3.4では、Control-Cを2回押すと動作しますが、多くの奇妙なスタックトレースがダンプされます。

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

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

私のユースケースは簡単です。ユーザーがControl-Cを押すと、スクリプトは正常に動作するスクリプトのようにすぐに終了します。それが私が望むすべてです。

それでは、_concurrent.futures_を使用するときにこの動作を取得する適切な方法は何ですか?

30
Nick Chammas

ちょっと痛いです。基本的に、メインスレッドを終了するには、ワーカースレッドを終了する必要があります。終了しないと終了できません。典型的な回避策は、各スレッドがより多くの作業を行うべきかどうかを判断するためにチェックできるグローバルな状態にすることです。

これが quote の理由の説明です。本質的に、インタープリターが終了するときにスレッドが終了すると、悪いことが起こる可能性があります。

これが実際の例です。子スレッドのスリープ期間のため、C-cの伝播には最大で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
16
cdosborn