Python threading.thread.start() не возвращает управление основной теме

Я пытаюсь выполнить программу, которая выполняет часть кода таким образом, что пользователь может остановить его выполнение в любое время без остановки основной программы. Я думал, что могу сделать это, используя threading.Thread, но затем я запустил следующий код в IDLE (Python 3.3):

from threading import *
import math
def f():
    eval("math.factorial(1000000000)")
t = Thread(target = f)
t.start()

Последняя строка не возвращается: я в конце концов перезапустил оболочку. Является ли это следствием блокировки Global Interpreter Lock, или я делаю что-то неправильно? Я не видел ничего конкретного в этой проблеме в документации по потокам (http://docs.python.org/3/library/threading.html)

Я попытался сделать то же самое, используя процесс:

from multiprocessing import *
import math
def f():
    eval("math.factorial(1000000000)")

p = Process(target = f)
p.start()
p.is_alive()

Последняя строка возвращает False, хотя я запускал ее только через несколько секунд после начала процесса! Основываясь на использовании моего процессора, я вынужден сделать вывод, что этот процесс никогда не начинался. Может кто-нибудь объяснить, что я делаю неправильно здесь?

Ответ 1

Thread.start() никогда не возвращается! Может ли это иметь какое-то отношение к реализации математической библиотеки C?

Как @eryksun указал в комментарии: math.factorial() реализуется как функция C, которая не выпускает GIL, поэтому никакой другой код Python не может работать до тех пор, пока он не вернется.

Примечание. multiprocessing версия должна работать так: каждый процесс Python имеет свой собственный GIL.


factorial(1000000000) имеет сотни миллионов цифр. Вместо этого попробуйте import time; time.sleep(10) как фиктивный расчет.

Если у вас есть проблемы с многопоточным кодом в IDLE, попробуйте тот же код из командной строки, чтобы убедиться, что ошибка сохраняется.

Если p.is_alive() возвращает False после того, как p.start() уже вызван, это может означать, что в f() есть ошибка, например, MemoryError.

На моей машине p.is_alive() возвращает True, а один из cpus - на 100%, если я вставляю свой код из вопроса в оболочку Python.

Несвязанный: удалить импорт подстановочных знаков, например from multiprocessing import *. Они могут затенять другие имена в вашем коде, чтобы вы не могли быть уверены в том, что означает определенное имя, например, threading может определить функцию eval (она не может это сделать) с аналогичной, но другой семантикой, которая может сломаться ваш код молча.

Я хочу, чтобы моя программа умела корректно обрабатывать смешные входы от пользователя

Если вы передаете пользовательский ввод непосредственно в eval(), пользователь может сделать что угодно.

Есть ли способ получить процесс для печати, скажем, сообщения об ошибке без создания канала или другой подобной структуры?

Это обычный код Python:

print(message) # works

Разница в том, что если несколько процессов выполняются print(), тогда выход может быть искажен. Вы можете использовать блокировку для синхронизации вызовов print().