Поток выполняется недостаточно быстро, когда запущены другие приложения

#python #python-multiprocessing #python-multithreading

#python #python-многопроцессорная обработка #python-многопоточность

Вопрос:

Я использую модуль threading для запуска функции в фоновом режиме, пока выполняется остальная часть моего скрипта. Потоковая функция содержит цикл for, который ожидает внешних 5-вольтовых триггеров, возникающих каждые 15 мс, прежде чем перейти к следующей итерации цикла.

Когда этот код — единственное, что работает на ПК, все работает так, как ожидалось. Однако, когда я запускаю другие необходимые приложения, нагружая процессор, цикл For в потоковой функции выполняется только и продолжается до следующей итерации в течение временного окна 15 мс примерно в 90% случаев.

Входными данными для потоковой функции является список указателей ctypes.

Я запускаю потоковую функцию из класса, поэтому использовать многопроцессорную обработку сложно (если это вообще поможет, я не уверен).

Я попытался проиллюстрировать проблему ниже с помощью скелета двух классов

 import ctypes
import Write_transient_frames_func
import SendScriptCommands
from threading import Thread

class SlmInterface():

    def __init__(self,sdk):

        self.sdk = sdk

    def precalculate_masks(self, mask_list):

        '''takes input mask_list, a list of numpy arrays containing phase masks
           outputs pointers to memory location of masks
        '''

        #list of pointers to locations of phase mask arrays in memory 
        mask_pointers = [mask.ctypes.data_as(POINTER(c_ubyte)) for mask in mask_list]

        return mask_pointers

    def load_precalculated_triggered(self, mask_pointers):

        okay = True
        print('Ready to trigger')

        for arr in mask_pointers:

            okay = self.Write_transient_frames_func(self.sdk, c_int(1), arr, c_bool(1), c_bool(1), c_uint(0))

        assert okay, 'Failed to write frames to board'      
        print('completed trigger sequence')


class Experiment():

    def run_experiment(self, sdk, mask_list):  

        slm = SlmInterface(sdk)

        #list of ctypes pointers      
        mask_pointers = slm.precalculate_masks(mask_list)

        ##the threaded function
        slm_thread = Thread(target=slm.load_precalculated_triggered, args = [mask_pointers])
        slm_thread.start()

        time.sleep(0.1)

        # this function loads the 15ms trigger sequences to the hardware and begins the sequence 
        self.mp_output = SendScriptCommands()
 

Возможно ли ускорить выполнение потоковой функции? Поможет ли параллельная обработка? Или я принципиально ограничен своим процессором?

Комментарии:

1. Вы используете параллельную обработку. Потоки находятся под контролем операционной системы. Я думаю, что ваше требование о времени отклика в 15 мс нереально.

2. Спасибо, есть ли альтернатива, которая позволила бы мне запустить функцию load_precalculated_triggered в фоновом режиме, а затем продолжить выполнение остальной части скрипта?

3. У меня была такая же проблема раньше, и я обнаружил, что мои потоки запускают циклы while и потребляют мощность процессора, позже я узнал, что мне нужно добавить ‘time.sleep (0.1)’ в качестве времени задержки внутри циклов, чтобы дать время другим потокам работать, и, что удивительно, моя загрузка процессораработает в обычном режиме, а потоки выполняются очень быстро, примечание: мое приложение представляет собой менеджер загрузок, который генерирует более 100 одновременных потоков за одну загрузку и может запускать одновременные загрузки одновременно без снижения скорости

4. Отключение на 100 мс в потоке, который пытается перехватить сигналы, которые происходят каждые 15 мс, только усугубит проблему. Теперь вам не хватает ~ 5 сигналов каждые 6 — отличная работа!

Ответ №1:

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

Ответ №2:

Вы должны знать о том факте, что многопоточность в python замедляет работу приложения. Хорошей альтернативой является использование asyncio, поскольку оно позволяет выполнять совместную многозадачность нескольких задач в одном потоке (-> ОС не нужно фактически переключать поток -> меньше накладных расходов -> более быстрое выполнение). Если вы не использовали это раньше, сначала это вроде как нужно использовать, но на самом деле это действительно приятно.

Однако ваша задача, похоже, действительно связана с процессором. Так что, возможно, единственным вариантом является многопроцессорная обработка в python.

Ответ №3:

Вероятно, Python на самом деле не виноват здесь. Дело в том, что в многопользовательских операционных системах общего назначения с преимущественным использованием вы не получите гарантии непрерывной работы, достаточной для обнаружения триггеров в течение любых 15 мс. Процессор выделяется в количествах, как правило, в несколько десятков мс, и ОС может — и будет — позволять вашему потоку выполняться более или менее часто в зависимости от загрузки процессора, стремясь предоставить каждому процессу его справедливую долю доступного процессорного времени.

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

Но на самом деле, на самом деле решение состоит в том, чтобы справиться с этим на более низком уровне, либо в режиме ядра, либо в аппаратном обеспечении. Опрос с такой скоростью из пользовательского режима нежелателен, если вы не можете пропустить сигнал, поэтому вам, вероятно, следует выяснить, предоставляет ли ваше оборудование / драйвер какой-либо интерфейс более высокого уровня — например, прерывание (переведенное, например, на разблокировку какого-либо блокирующего вызова или выдачу сигнала или что-то в этом роде) при запуске.