Моя команда разрабатывает приложение GTK-Python, которое динамически создает потоки для обработки более длительных задач. Недавно мы добавили функцию запроса некоторых пакетных вычислений по требованию, и из-за этого графический интерфейс перестал отвечать на запросы. Мои исследования и анализ показывают, что проблема, по-видимому, заключается в конкуренции за GIL, которая приводит к истощению потока графического интерфейса. Самое дешевое решение, которое мы рассматриваем, — это переместить наши пользовательские потоки в другой процесс/GIL, чтобы потоки, создаваемые средой графического интерфейса, выполнялись в одном процессе, а наши динамически создаваемые потоки выполнялись в другом процессе.
Я читал документацию по многопроцессорной обработке, и мне не приходило в голову ничего, что поддерживало бы такую функцию «из коробки». Какие варианты мне следует изучить?
Если вы не хотите создавать по одному процессу для каждого интерпретатора (что делает multiprocessing), вы можете посмотреть docs.python.org/3/whatsnew/…
@Ohm'sLawman под «динамически создаваемым» я имею в виду потоки, создаваемые по требованию для обработки запроса пользователя. (Миграция потоков не подразумевается.) Может ли процесс, созданный модулем multiprocessing, создавать потоки по требованию? Это та самая «такая особенность», на которую я намекал.
В процессе, созданном multiprocessing, нет ничего особенного. Он может делать все то же самое, что и любой другой процесс Python. Однако особенностью является то, что связь между процессами ограничена. Вы не можете просто обмениваться объектами между потоками так, как вы это делаете.* Сообщения, которые отправляет ваш процесс графического интерфейса, и ответы, которые возвращаются, должны быть в форме объектов, которые можно замариновать и передать через канал. или Очередь.
*Определенные примитивные типы, очевидно, могут использоваться разными процессами в определенных операционных системах. docs.python.org/3/library/… Но у меня нет в этом опыта






Вот одна из возможностей. Вы создаете дочерний процесс thread_creator, целью которого является создание дочерних потоков. Это достигается путем передачи thread_creator экземпляра multiprocessing.Queue, который будет содержать кортежи, определяющие аргументы для создания нового дочернего потока. Специальное контрольное значение None является сигналом для завершения процесса thread_creator. Когда все созданные им потоки, не являющиеся демонами, завершаются, дочерний процесс завершается, а вместе с ним и все созданные им потоки демона.
В приведенном ниже коде мы создаем поток, не являющийся демоном sample_threaded_worker, который завершается через 0,5 секунды, и поток-демон another_threaded_worker, который непрерывно печатает сообщение каждые 0,1 секунды. После того, как thread_creator_process будет предложено завершить работу, он сделает это, как только sample_threaded_worker завершится. Когда это произойдет, действие another_threaded_worker будет автоматически прекращено. Таким образом, another_threaded_worker будет выполняться примерно 0,5 секунды и распечатает примерно 5 сообщений:
from multiprocessing import Process, Queue
from threading import Thread
import time
def sample_threaded_worker(x=1, y=2, debug=False):
time.sleep(.5)
if debug:
print('sample_threaded_worker', x, y)
def another_threaded_worker():
while True:
print('another_threaded_worker', time.time())
time.sleep(.1)
def thread_creator(queue):
while True:
request = queue.get()
if request is None:
break
# Unpack
target, args, kwargs, daemon = request
Thread(target=target, args=args, kwargs=kwargs, daemon=daemon).start()
def create_thread(queue, target=None, args=(), kwargs = {}, daemon=None):
"""Helper function to create a child thread in the child process."""
queue.put((target, args, kwargs, daemon))
def main():
queue = Queue()
p = Process(target=thread_creator, args=(queue,))
p.start()
create_thread(queue, target=sample_threaded_worker, args=(5, 9), kwargs = {'debug': True})
create_thread(queue, target=another_threaded_worker, daemon=True)
# Terminate process. All daemon threads that have been created will be killed
# when all non-daemon threads have completed.
queue.put(None)
p.join()
if __name__ == '__main__':
main()
Распечатки:
another_threaded_worker 1718880689.7697933
another_threaded_worker 1718880689.87115
another_threaded_worker 1718880689.9717884
another_threaded_worker 1718880690.0727093
another_threaded_worker 1718880690.1730008
sample_threaded_worker 5 9
Что означает «динамически создаваемый» и как это связано с «такой функцией»? Если вы надеетесь найти какой-то способ переместить уже созданный поток из одного процесса в другой, то, к сожалению, этого не произойдет.