2009-07-30 1 views
0

Я использую python для взаимодействия аппаратного устройства с USB-интерфейсом с API-интерфейсом python, предоставляемого поставщиком, и я пытаюсь читать (пакеты usb) с устройства в отдельном потоке в бесконечном цикле (который отлично работает). Проблема в том, что мой основной цикл, кажется, никогда не получает запланированных снова (мой цикл чтения получает все внимание).Проблемы с расписанием в python

Код выглядит так же, как это:

from threading import Thread 
import time 
usb_device = 0 

def usb_dump(usb_device): 
    while True: 
     #time.sleep(0.001) 
     packet = ReadUSBDevice(usb_device) 
     print "packet pid: %s" % packet.pid 

class DumpThread(Thread): 
    def run(self): 
     usb_dump() 

usb_device = OpenUSBDevice() 
t = DumpThread() 
t.start() 
print "Sleep 1" 
time.sleep(1) 
print "End" 
CloseUSBDevice(usb_device) 
sys.exit(0) 

(я мог вставить фактический код, но так как вам нужно аппаратное устройство, я полагаю, что это не поможет).

Я ожидаю, что этот код начнет сбрасывать пакеты USB примерно за секунду до того, как основной поток завершит работу всей программы. Однако все, что я вижу, это «Сон 1», а затем процедура usb_dump() выполняется навсегда. Если я раскомментирую инструкцию «time.sleep (0.001)» во внутреннем цикле процедуры usb_dump(), все начнет работать так, как я ожидаю, но тогда код python не сможет идти в ногу со всеми входящими пакетами :-(

продавец говорит мне, что это проблема питон планировщик, а не вина их API, и для этого мне не поможет:.

«Тем не менее, кажется, что вы испытываете некоторые нюансы при использовании многопоточности в Python по поместив time.sleep в поток DumpThread, вы явно сигнализируете системе потоковой передачи Python, чтобы отказаться от управления. В противном случае интерпретатор Python должен определить, когда переключать потоки, и обычно это делает после определенного количество команд байтового кода выполнено ».

Может кто-нибудь подтвердить, что проблема python здесь? Есть ли другой способ сделать элемент управления DumpThread? Любые другие идеи?

ответ

3

Ваш продавец будет прав, если ваш был чистый python код; однако C-расширения могут освобождать GIL и, следовательно, позволяет выполнять многопоточность.

В частности, time.sleep делает освобождает GIL (вы можете проверить его непосредственно из исходного кода, here - посмотреть на floatsleep реализации); поэтому ваш код не должен иметь никаких проблем. В качестве дополнительного доказательства, я сделал также простой тест, просто удалив вызовы на USB, и это на самом деле работает, как ожидалось:

from threading import Thread 
import time 
import sys 

usb_device = 0 

def usb_dump(): 
    for i in range(100): 
     time.sleep(0.001) 
     print "dumping usb" 

class DumpThread(Thread): 
    def run(self): 
     usb_dump() 

t = DumpThread() 
t.start() 
print "Sleep 1" 
time.sleep(1) 
print "End" 
sys.exit(0) 

Наконец, всего пару нот на код, который вы размещены:

  • usb_device не передается в поток. Вам нужно передать его в качестве параметра или (argh!) Сообщить потоку, чтобы получить его из глобального пространства имен.
  • Вместо того чтобы принудительно использовать sys.exit(), было бы лучше просто остановить поток, а затем закрыть USB-устройство. Я подозреваю, что ваш код может получить многопоточную проблему, как сейчас.
  • Если вам нужен только периодический опрос, класс threading.Timer может быть лучшим решением для вас.

[Update] О последнем пункте: как сказано в комментарии, я думаю, что Timer бы лучше соответствовать семантический вашей функции (периодический опрос) и автоматически избежать проблем с GIL не выпускается по коду поставщика.

+0

На заметках: Я знаю, что код выглядит уродливым. Я начал с примера кода от поставщика и сделал как можно меньше изменений, пока еще показываю свою точку зрения. Я решил, что они будут знакомы с их собственным примером ... Это вовсе не производственный код. –

+0

Итак, если я предполагаю, что расширение поставщика C (которое лежит за вызовом ReadUSBDevice) не освобождает GIL (как следует), то добавление небольшого сна (как я пытался) заставит код работать должным образом, потому что теперь Я явно освобождаю GIL на каждой итерации. Это объясняет поведение, которое я вижу. Есть ли другой способ освободить GIL? Возможно, я смогу сделать, если это будет спать каждые 50 раундов? –

+0

Ответственность вашего поставщика - освободить GIL, и вы не можете этого сделать. Как насчет использования объекта threading.Timer? Он автоматически реализует как семантику вашей функции (периодический опрос), так и вам не придется заботиться об их реализации. –

0

Я думаю, что продавец прав. Предполагая, что это CPython, нет истинной параллельной резьбы; только один поток может выполняться одновременно. Это связано с реализацией global interpreter lock.

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

Другая возможность, которая может помочь, - изменить планировщика.

2

Я предполагаю, что вы написали модуль Python C, который предоставляет функцию ReadUSBDevice, и что он предназначен для блокировки до получения пакета USB, а затем возвращает его.

Реальная реализация ReadUSBDevice должна выпустить Python GIL, пока он ждет USB-пакет, а затем повторно получает его, когда он его получает. Это позволяет запускать другие потоки Python во время выполнения собственного кода.

http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock

В то время как вы отпер GIL, вы не можете получить доступ к Python. Отпустите GIL, запустите функцию блокировки, затем, когда вы знаете, что у вас есть что-то, чтобы вернуться на Python, повторно приобретите его.

Если вы этого не сделаете, тогда никакие другие потоки Python не смогут выполнить, пока происходит ваша блокировка. Если это модуль Python, поставляемый поставщиком, отказ в выпуске GIL во время активной блокировки - ошибка.

Обратите внимание, что если вы получаете много пакетов и фактически обрабатываете их в Python, тогда все еще должны выполняться другие потоки. Несколько потоков, которые фактически запускают код Python, не будут выполняться параллельно, но часто будут переключаться между потоками, что дает им возможность запускать все. Это не работает, если собственный код блокируется, не отпуская GIL.

Редактировать: Я вижу, вы упомянули, что это библиотека, поставляемая поставщиком. Если у вас нет источника, быстрый способ узнать, выпускают ли они GIL: запустите поток ReadUSBDevice, пока активность USB не происходит, поэтому ReadUSBDevice просто сидит в ожидании данных. Если они выпускают GIL, другие потоки должны работать беспрепятственно. Если это не так, это заблокирует весь интерпретатор. Это было бы серьезной ошибкой.

+0

Я забыл упомянуть: ReadUSBDevice - это модуль python c (как и ожидалось), и он имеет (по умолчанию) тайм-аут 500 мс, поэтому он ждет пакетов в полсекунды, а затем возвращается. Я предполагаю, что он выпускает Python GIL (что бы то ни было, будет читать об этом) для каждой итерации. Однако, поскольку я запускаю бесконечный цикл, поток только начинается в другом цикле. Python все еще должен был запланировать основной цикл для каждой итерации, не так ли? –

+0

Он должен иметь возможность запускать любые другие потоки Python в течение всего таймаута 500 мс, если он освобождает GIL, как он должен. Если это не так, я ожидаю, что он выйдет * в конце концов *, но, возможно, только после десятков итераций за тайм-аут 500 мс. –

+0

Непонятно, что вы отметили ответ, который пришел через час с меньшим количеством информации, чем это решение. –