Выполнение потоков без графического интерфейса в отдельном GIL

Моя команда разрабатывает приложение GTK-Python, которое динамически создает потоки для обработки более длительных задач. Недавно мы добавили функцию запроса некоторых пакетных вычислений по требованию, и из-за этого графический интерфейс перестал отвечать на запросы. Мои исследования и анализ показывают, что проблема, по-видимому, заключается в конкуренции за GIL, которая приводит к истощению потока графического интерфейса. Самое дешевое решение, которое мы рассматриваем, — это переместить наши пользовательские потоки в другой процесс/GIL, чтобы потоки, создаваемые средой графического интерфейса, выполнялись в одном процессе, а наши динамически создаваемые потоки выполнялись в другом процессе.

Я читал документацию по многопроцессорной обработке, и мне не приходило в голову ничего, что поддерживало бы такую ​​функцию «из коробки». Какие варианты мне следует изучить?

Что означает «динамически создаваемый» и как это связано с «такой функцией»? Если вы надеетесь найти какой-то способ переместить уже созданный поток из одного процесса в другой, то, к сожалению, этого не произойдет.

Ohm's Lawman 17.06.2024 18:01

Если вы не хотите создавать по одному процессу для каждого интерпретатора (что делает multiprocessing), вы можете посмотреть docs.python.org/3/whatsnew/…

Michael Butscher 17.06.2024 18:01

@Ohm'sLawman под «динамически создаваемым» я имею в виду потоки, создаваемые по требованию для обработки запроса пользователя. (Миграция потоков не подразумевается.) Может ли процесс, созданный модулем multiprocessing, создавать потоки по требованию? Это та самая «такая особенность», на которую я намекал.

Olumide 17.06.2024 18:11

В процессе, созданном multiprocessing, нет ничего особенного. Он может делать все то же самое, что и любой другой процесс Python. Однако особенностью является то, что связь между процессами ограничена. Вы не можете просто обмениваться объектами между потоками так, как вы это делаете.* Сообщения, которые отправляет ваш процесс графического интерфейса, и ответы, которые возвращаются, должны быть в форме объектов, которые можно замариновать и передать через канал. или Очередь.

Ohm's Lawman 17.06.2024 18:16

*Определенные примитивные типы, очевидно, могут использоваться разными процессами в определенных операционных системах. docs.python.org/3/library/… Но у меня нет в этом опыта

Ohm's Lawman 17.06.2024 18:20
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
5
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот одна из возможностей. Вы создаете дочерний процесс 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

Другие вопросы по теме