Поскольку анонимные каналы являются наиболее традиционным инструментом, мы познакомимся с ними в первую очередь. Сценарий в примере 5.19 создает копию вызывающего процесса с помощью функции os.fork (с ветвлением процессов мы познакомились выше в этой главе). После ветвления исходный родительский процесс и его дочерняя копия общаются между собой через канал, созданный функцией os.pipe перед ветвлением. Функция os.pipe возвращает кортеж с двумя дескрипторами файлов — низкоуровневыми идентификаторами файлов, с которыми мы познакомились в главе 4, представляющими входной и выходной концы канала. Так как ответвленный дочерний процесс получает копии дескрипторов файлов своего родителя, то при записи в дескриптор выходного конца канала в дочернем процессе данные посылаются обратно родителю по каналу, созданному до создания дочернего процесса.
Пример 5.19. PP4E\System\Processes\pipe1.py
import os, time
def child(pipeout):
zzz = 0
while True:
time.sleep(zzz) # заставить родителя подождать
msg = (‘Spam %03d’ % zzz).encode() # каналы — двоичные файлы os.write(pipeout, msg) # отправить данные родителю
zzz = (zzz+1) % 5 # переход к 0 после 4
print(‘Parent %d got [%s] at %s’ % (os.getpid(), line, time.time()))
parent()
Если запустить эту программу в Linux, Cygwin или в другой Unix— подобной системе (функция pipe имеется в стандартной реализации Python для Windows, а вот функция fork — нет), то родительский процесс при каждом вызове os.read будет ждать, пока дочерний процесс отправит данные в канал. Здесь дочерний и родительский процессы действуют почти как клиент и сервер — родитель запускает дочерний процесс и ждет от него инициации обмена.[XVIII] Для имитации длительных операций дочерний процесс заставляет родителя ждать каждое следующее сообщение на одну секунду дольше предыдущего с помощью вызова функции time.sleep, пока задержка не достигнет четырех секунд. Когда счетчик задержки zzz становится равным 005, он сбрасывается обратно в 000, и отсчет начинается сначала:
[C:\…\PP4E\System\Processes]$ python pipe1.py
Parent 6716 |
got |
[b’Spam 000’] |
at |
1267996104.53 |
Parent 6716 |
got |
[b’Spam 001’] |
at |
1267996105.54 |
Parent 6716 |
got |
[b’Spam 002’] |
at |
1267996107.55 |
Parent 6716 |
got |
[b’Spam 003’] |
at |
1267996110.56 |
Parent 6716 |
got |
[b’Spam 004’] |
at |
1267996114.57 |
Parent 6716 |
got |
[b’Spam 000’] |
at |
1267996114.57 |
Parent 6716 |
got |
[b’Spam 001’] |
at |
1267996115.59 |
Parent 6716 |
got |
[b’Spam 002’] |
at |
1267996117.6 |
Parent 6716 |
got |
[b’Spam 003’] |
at |
1267996120.61 |
Parent 6716 |
got |
[b’Spam 004’] |
at |
1267996124.62 |
Parent 6716 |
got |
[b’Spam 000’] |
at |
1267996124.62 |
Parent 6716 |
got |
[b’Spam 001’] |
at |
1267996125.63 |
…и так далее: Ctrl—C для выхода…
Обратите внимание, что родитель принимает из канала строку байтов. Данные через простые каналы обычно передаются в виде строк байтов, если они обслуживаются с применением инструментов для работы с дескрипторами файлов, с которыми мы встречались в главе 4 (как мы видели там, инструменты чтения из дескрипторов и записи в дескрипторы, имеющиеся в модуле os, всегда возвращают и принимают строки байтов). Именно поэтому мы вынуждены в дочернем процессе вручную кодировать текст в строку байтов перед записью в канал — операция форматирования строк не может применяться к строкам байтов. Как будет показано в следующем разделе, дескриптор канала можно обернуть объектом текстового файла, как мы делали это в примерах главы 4, но этот прием обеспечит лишь автоматическое кодирование и декодирование при передаче данных средствами объекта, тогда как внутри канала данные все равно будут передаваться в форме строк байтов.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011