Как мы видели выше, когда сценарии порождают потоки выполнения — задачи, выполняемые параллельно внутри программы, — потоки могут естественным образом поддерживать связь друг с другом путем изменения и чтения переменных и объектов в совместно используемой глобальной памяти. Сюда относятся как доступные переменные и атрибуты, так и изменяемые объекты. Мы видели также, что следует позаботиться об использовании блокировок для синхронизации доступа к совместно используемым объектам, если есть вероятность одновременного их изменения из разных потоков. Потоки выполнения предлагают достаточно простую модель взаимодействий, и модуль queue во многих ситуациях реализует синхронизацию практически автоматически.
Все становится намного сложнее, когда сценарии запускают дочерние процессы и программы, вообще не имеющие совместно используемой памяти. Если определить виды взаимодействий, которые могут осуществляться между программами, то окажется, что большинство вариантов мы уже рассмотрели в этой и в предыдущих главах. Например, ниже перечислены простые механизмы, которые могут рассматриваться, как инструменты взаимодействий между программами:
• Простые файлы
• Аргументы командной строки
• Коды завершения программ
• Переадресация стандартных потоков ввода-вывода
• Каналы, создаваемые с помощью функции os.popen и модуля subprocess
Например, передача параметров в командной строке и запись в потоки ввода позволяет передавать параметры выполнения программ; чтение потоков вывода и кодов завершения дает возможность получать результаты. Поскольку порождаемыми программами наследуются значения переменных окружения, их также можно рассматривать, как один из способов передачи контекста. Каналы, создаваемые при помощи функции os.popen или модуля subprocess, позволяют организовать еще более динамические взаимодействия: данные могут передаваться между программами в произвольные моменты времени, не только во время запуска или завершения.
Помимо этих механизмов в библиотеке Python есть и другие средства организации взаимодействий между процессами (Inter—Process Communication, IPC). К ним относятся сокеты, разделяемая память, сигналы, анонимные и именованные каналы и другие. Некоторые из них являются более переносимыми, некоторые менее переносимыми, и все они различаются по сложности и сфере использования. Например:
• Сигналы позволяют программам передавать простые уведомления другим программам.
• Анонимные каналы позволяют обмениваться данными потокам выполнения и родственным процессам, совместно использующим файловые дескрипторы, но этот механизм опирается на модель ветвления процессов в Unix-подобных системах, которая не является переносимой.
• Именованные каналы отображаются в файловую систему — они позволяют обмениваться данными полностью независимым программам, но они доступны в Python не на всех платформах.
Хотя некоторые из них могут использоваться, как механизмы взаимодействий между потоками выполнения, но истинная их мощь становится видна, когда они используются для организации взаимодействий между отдельными процессами, вообще не имеющими общей памяти.
В данном разделе мы познакомимся с каналами (анонимными и именованными), а также с сигналами. Помимо этого здесь мы впервые встретимся с сокетами, но только в виде предварительного знакомства. Сокеты могут использоваться для организации взаимодействий процессов, выполняющихся на одном компьютере, но так как основное их назначение заключается в работе с сетями, большую часть подробностей мы оставим до части книги, где будет рассказываться о разработке приложения для Интернета.
Программисты на языке Python могут пользоваться и другими механизмами IPC (например, разделяемой памятью, доступ к которой предоставляется модулем mmap), о которых здесь не рассказывается из-за недостатка места. Если вас интересует что-то более специальное, ищите в руководствах Python и на веб-сайте подробности использования других схем IPC.
По окончании этого раздела мы также исследуем пакет multiprocessing, который предлагает дополнительные и переносимые механизмы IPC, являющиеся частью его универсального API запуска процессов, включая разделяемую память, а также каналы и очереди для передачи произвольных объектов Python в сериализованном виде. Но сначала познакомимся с более традиционными подходами.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, I том, 2011