Подтвердить что ты не робот

Как вы убиваете фьючерсы после их запуска?

Я использую новый concurrent.futures модуль (который также имеет Python 2 backport), чтобы сделать несколько простых многопоточных операций ввода-вывода. У меня возникли проблемы с пониманием того, как очистить задачи, запущенные с помощью этого модуля.

Проверьте следующий Python 2/3 script, который воспроизводит поведение, которое я вижу:

#!/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()

Пока этот script работает, кажется невозможным убить чистое использование обычного прерывания клавиатуры Control-C. Я запускаю OS X.

  • На Python 2.7 мне нужно прибегнуть к kill из командной строки, чтобы убить script. Control-C просто игнорируется.
  • В Python 3.4 Control-C работает, если вы нажмете его дважды, но затем выгрузите много странных трасс стека.

Большинство документов, которые я нашел в Интернете, говорят о том, как очистить потоки от старого threading модуля. Ничто из этого, похоже, не применяется здесь.

И все методы, предоставляемые в модуле concurrent.futures для остановки файла (например, Executor.shutdown() и Future.cancel()), работают только тогда, когда Фьючерсы еще не начаты или завершены, что в данном случае бессмысленно. Я хочу немедленно прервать будущее.

Мой пример использования прост: когда пользователь нажимает Control-C, script должен немедленно выйти, как и любой хорошо выполненный script. Это все, что я хочу.

Итак, какой правильный способ получить это поведение при использовании concurrent.futures?

4b9b3361

Ответ 1

Это вроде больно. По сути, ваши рабочие потоки должны быть закончены, прежде чем ваш основной поток сможет выйти. Вы не можете выйти, если они этого не сделают. Типичным обходным решением является наличие некоторого глобального состояния, которое каждый поток может проверить, чтобы определить, должны ли они делать больше работы или нет.

Здесь 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