#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 мс. Процессор выделяется в количествах, как правило, в несколько десятков мс, и ОС может — и будет — позволять вашему потоку выполняться более или менее часто в зависимости от загрузки процессора, стремясь предоставить каждому процессу его справедливую долю доступного процессорного времени.
Вы можете увеличить приоритет своего потока, чтобы запросить у него приоритет над другими, или, в крайнем случае, изменить его на приоритет в реальном времени, чтобы позволить ему загружать процессор на неопределенный срок (и потенциально зависать в системе, если что-то пойдет не так).
Но на самом деле, на самом деле решение состоит в том, чтобы справиться с этим на более низком уровне, либо в режиме ядра, либо в аппаратном обеспечении. Опрос с такой скоростью из пользовательского режима нежелателен, если вы не можете пропустить сигнал, поэтому вам, вероятно, следует выяснить, предоставляет ли ваше оборудование / драйвер какой-либо интерфейс более высокого уровня — например, прерывание (переведенное, например, на разблокировку какого-либо блокирующего вызова или выдачу сигнала или что-то в этом роде) при запуске.