Многопроцессорность с серийным объектом в качестве параметра

У меня проблема с Python и я передаю серийный объект в качестве параметра отдельному процессу. Программа запускается в Windows 8, поэтому использование глобальных переменных не вариант.

from multiprocessing import Queue
from multiprocessing import Process
import os
import serial
from serial.tools import list_ports
from time import sleep

displayMessages = Queue()
modemPort = None

def processDisplayMessages(displayMessages):
    while True:
        msg = displayMessages.get()  #should halt until message in queue
        print msg

def processIncomingSerialMessages(modemPort, displayMessages):
    while True:
        line = modemPort.readline()
        displayMessages.put(line)

def main():
    print "Serial Send Test"
    Process(target=processDisplayMessages, args = (displayMessages,)).start()
    modemPort = serial.Serial('COM5', 57600, timeout=0.9)  # open first serial port
    Process(target=processIncomingSerialMessages, args = (modemPort, displayMessages)).start()
    print "Back from launch"

    sleep(0.1)

 if __name__ == '__main__':
    main()

Когда программа запускается, я получаю следующую ошибку:

Process Process-2:
Traceback (most recent call last):
  File "c:\python27\lib\multiprocessing\process.py", line 258, in _bootstrap
    self.run()
  File "c:\python27\lib\multiprocessing\process.py", line 114, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\matthew.parets\Documents\..Development\RaspberryPi\windows\seri
alRecvPrototype.py", line 27, in processIncomingSerialMessages
    line = modemPort.readline()
  File "c:\python27\lib\site-packages\serial\serialwin32.py", line 246, in read
    if not self.hComPort: raise portNotOpenError
AttributeError: 'Serial' object has no attribute 'hComPort'

Если я помещаю открытый для последовательного порта (modemPort) в качестве первой строки processIncomingSerialMessages, программа работает нормально. Проблема в том, что мне нужно разделить ввод и вывод модема, поэтому мне нужно передать серийный объект в качестве параметра. А Python, похоже, это не нравится.

Может ли кто-нибудь увидеть мою ошибку или предложить альтернативу?


person codingCat    schedule 01.01.2014    source источник


Ответы (1)


Я не могу запустить этот код, но я был бы удивлен, если бы он сработал: аргумент, передаваемый между процессами, работает, обрабатывая объект аргумента на стороне отправки, отправляя строку рассола между процессами через канал или сокет и распаковывая это строка на принимающей стороне. Я не знаю ни одного случая с открытыми объектами ввода-вывода, для которых это могло бы работать (файлы, сокеты, каналы ...). Типы объектов ввода-вывода не просто имеют внутреннее состояние данных, они также связаны с ресурсами, которые сам Python не реализует. Рассол - это просто поток необработанных байтов.

Вы уже догадались, что вам нужно открыть последовательный порт в рабочем процессе. Увы, я не знаю, что означает «Мне нужно разделить ввод и вывод от модема», поэтому трудно предложить обходной путь. Но я уверен, что вы поймете это, если примете то, что вы уже обнаружили на собственном горьком опыте: передача открытого последовательного объекта между процессами никогда не сработает.

Тем не менее, вы можете покопаться в различных протоколах травления и создать свой собственный класс с настраиваемым кодом травления / распаковки, который (повторно) открывал серийный объект, когда его не отбирали. Это был бы сложный способ скрыть то, что в противном случае было бы простым кодом для (повторного) открытия последовательного объекта в рабочих процессах.

РЕДАКТИРОВАТЬ: вопросы и ответы

И снова Windows не предлагает простого выхода из глобальных переменных, поэтому я застрял в одном процессе, обрабатывающем и отправку, и получение.

«Глобальная переменная», вероятно, вам не поможет. Я предполагаю, что вы имеете в виду fork(), но ничто не является общим для fork(): дочерние процессы видят копии адресного пространства родительского процесса. Уловки ввода-вывода также часто не работают правильно.

Предоставляет ли Python способ передать значение по ссылке или ссылочное значение в процесс? Я попытался добавить серийный объект в списки и наборы с теми же результатами. Опять же, предоставляет ли Python что-то вроде объекта или массива из Java, через которые я мог бы получить ссылку, не будучи «маринованным»?

Во всех современных ОС существует очень высокая стена между процессами. Чтобы что-то действительно разделялось между процессами, вам нужно использовать для этого типы, созданные с нуля, или использовать средства «разделяемой памяти» ОС.

Вы можете прочитать документацию для multiprocessing.sharedctypes, в которой описаны способы использования разделяемой (между процессами) памяти. Но, как предупреждают документы:

Примечание. Хотя можно сохранить указатель в общей памяти, помните, что это будет относиться к месту в адресном пространстве определенного процесса. Однако указатель, скорее всего, окажется недействительным в контексте второго процесса, и попытка разыменования указателя из второго процесса может вызвать сбой.

Это не сработает. Это не проблема языка программирования, это проблема ОС. sharedctypes полезен для таких вещей, как массивы int и float.

Чтобы это приложение работало (телеметрия в реальном времени), процесс приема должен постоянно оставаться в рабочем состоянии.

Извините, я не слежу за этим в контексте. В своем вопросе вы сказали: «Если я помещу открытый для последовательного порта (modemPort) в первую строку processIncomingSerialMessages, программа будет работать нормально». processIncomingSerialMessages имеет бесконечный цикл после этого. В каком смысле это нарушает «процесс приема должен оставаться в рабочем состоянии все время»?

В показанном вами коде не кажется существенной разницы, открыт ли последовательный порт в рабочем процессе или в основном процессе (если последний действительно работал), и действительно, вы сказал, что все будет нормально, если вы сделаете это по-прежнему. Так что же плохого в том, чтобы делать это таким образом? В самом деле, зачем вообще использовать для этого рабочий процесс? Основная программа, которую вы показали, ничего не делает (кроме сна на десятые доли секунды) после запуска обоих рабочих процессов, так почему бы не позволить основной программе быть "процессом приема"?

person Tim Peters    schedule 02.01.2014
comment
Разделение: размещение ввода и вывода в отдельные процессы. Чтобы это приложение работало (телеметрия в реальном времени), процесс приема должен постоянно оставаться в рабочем состоянии. Выключение порта может привести к потере данных, и я мог бы также справиться с отправкой и получением в одном и том же процессе. И снова Windows не предлагает простого выхода из глобальных переменных, поэтому я застрял в одном процессе, обрабатывающем и отправку, и получение. - person codingCat; 02.01.2014
comment
Интересны подробности того, как Python обрабатывает передачу аргументов. Если я правильно читаю ваше описание, маринованные переменные отправляются по значению, и что-то связанное с вводом-выводом, такое как мой серийный объект, не выдержит передачи. Предоставляет ли Python способ передать значение по ссылке или ссылочное значение в процесс? Я попытался добавить серийный объект в списки и наборы с теми же результатами. Опять же, предоставляет ли Python что-то вроде объекта или массива из Java, где я мог бы получить ссылку, не будучи маринованной? - person codingCat; 02.01.2014
comment
Пример кода - это просто прототип. Я пытаюсь хэшировать последовательную связь с несколькими процессами, прежде чем добавить ее в основную программу. По своей природе как прототипа этот пример совершенно бессмыслен. Читается процесс чтения. Процесс записи пишет. И отобразится процесс отображения. Ничего больше. В полном приложении процесс чтения будет непрерывно считывать данные в любое время без пауз. Я хочу избежать сложности записи в процессе, сохраняя его простым и гарантируя, что все чтения будут приняты и сохранены. Таким образом, повод писать в другом месте - person codingCat; 02.01.2014
comment
Создание нового процесса в реализации Python на других платформах создает подпроцесс, который имеет доступ ко всем данным родительского процесса. Таким образом, простая глобальная переменная будет доступна из любого процесса, если программа работает в Linux. Windows не выполняет подпроцессы. Каждый новый процесс является чистым, с нуля, без передачи данных из процесса порождения. Следовательно, необходимо передавать параметры в процесс с общими данными. Это отлично работает со структурой данных, такой как Queue, но по разным причинам не работает для структур с привязкой к оборудованию, таких как последовательный порт. - person codingCat; 02.01.2014
comment
Извините, но вы не указали причину, по которой процесс чтения не может открыть порт, с которого выполняется чтение. Это очевидный способ сделать это, и он будет работать нормально. - person Tim Peters; 02.01.2014
comment
Нет, в Linux с Queue.Queue. Он работает с multiprocessing.Queue, потому что это пример типа, созданного с нуля для этого (межпроцессное взаимодействие), а multiprocessing.Queue - довольно сложный зверь под прикрытием, чтобы поддерживать это. - person Tim Peters; 02.01.2014
comment
Python и ОС блокируют доступ нескольких ресурсов к одному и тому же последовательному файлу. Когда один процесс открывает его для чтения, другой не может открыть его для записи. Он должен быть открыт в одном месте, а затем использоваться совместно. - person codingCat; 02.01.2014
comment
Ага! Я этого не осознавал. Тогда тебе не повезло. Однако это было бы легко сделать с помощью потоков (в рамках одного процесса). Вы об этом думали? - person Tim Peters; 02.01.2014
comment
И да, это работает. У меня есть работающий прототип на стороне Linux, который открывает последовательное соединение как первое задание в качестве глобальной переменной. Эта переменная, находящаяся в области общего доступа, легко доступна для всех процессов. Он вообще не передается в качестве параметра. И да, я знаю о необходимости использования multiprocessing.Queue, как показывает приведенный выше прототип. - person codingCat; 02.01.2014
comment
Я не был абсолютным в отношении глобальной переменной - я сказал, что уловки ввода-вывода также часто не работают правильно. Приятно, что он работал в Linux, но нет гарантии, что он будет работать и в Windows (если бы в Windows было fork()). Это намного сложнее, чем я думаю, вы сейчас оцениваете ;-) - person Tim Peters; 02.01.2014
comment
Я выбрал отдельные процессы, чтобы избежать пошагового поведения и блокировки от другого оборудования, с которым будет взаимодействовать окончательная программа. Потоки могут быть решением проблемы общих ресурсов, но для этого приложения лучшим решением, вероятно, будет единый последовательный процесс с тщательно сбалансированной отправкой и получением, который избегает блокировки любой ценой ... что представляет собой гораздо больше времени на разработку. : - / - person codingCat; 02.01.2014