Чтобы избежать задержки вывода или взаимоблокировки, сценарии, которые должны отправлять данные ожидающим программам за счет вывода в файлы-обертки сокетов (то есть с помощью print или sys. stdout.write), должны предусматривать выполнение одного из следующих пунктов:
• Периодически вызывать sys.stdout.flush, чтобы вытолкнуть содержимое буфера и обеспечить его отправку по мере вывода, как показано в примере 12.11.
• Запускаться с ключом —u интерпретатора Python, если это возможно, чтобы принудительно отключить буферизацию потоков вывода. Этот прием может применяться к немодифицированным программам, порожденным с помощью инструментов для работы с каналами, таких как os.popen. Но он не поможет в данном случае, потому что мы вручную переустанавливаем файлы потоков ввода-вывода, назначая им буферизованные текстовые файлы-обертки сокетов уже после того, как процесс будет запущен. Чтобы убедиться в этом, раскомментируйте вызовы flush в примере 12.11 и вызов sleep в конце и запустите его с ключом —u: вывод первого теста по-прежнему появится с задержкой в 5 секунд.
• Использовать по то ки вы пол не ния, чтобы избежать блокирования при чтении из сокетов, что особенно важно, если принимающей программой является графический интерфейс, который не должен зависеть от вызова метода flush на стороне клиента. Дополнительные указания вы найдете в главе 10. В действительности этот подход не решает проблему — порожденный поток выполнения, производящий чтение, также может оказаться заблокированным или взаимно заблокировать программу, выполняющую запись, однако в такой ситуации графический интерфейс хотя бы останется активным.
• Реализовать собственные, не стан дарт ные объекты-обертки сокетов, которые будут перехватывать операции записи текста, кодировать его в двоичное представление и передавать методу send сокета. Метод socket.makefile — это, в действительности, всего лишь инструмент для удобства, и мы всегда можем реализовать собственную обертку с более специализированными возможностями. Для подсказки смотрите реализацию класса GuiOutput в главе 10, класс перенаправления потоков ввода-вывода в главе 3 и классы в стандартном модуле io (на которых основаны инструменты ввода-вывода языка Python и которые можно подмешивать в собственные классы).
• Вообще не использовать print и выполнять обмен данными с применением «родных» интерфейсов инструментов IPC, таких как низкоуровневые методы сокетов send и recv, — они выполняют передачу данных немедленно и не предусматривают их буферизацию, как методы файлов. Таким способом мы можем непосредственно передавать простые строки байтов или использовать инструменты dumps и loads модуля pickle для преобразования объектов Python в строки байтов и обратно при передаче их непосредственно через сокеты (подробнее о модуле pickle рассказывается в главе 17).
Последний вариант является наиболее прямым (кроме того, функции перенаправления из вспомогательного модуля возвращают простые сокеты как раз для поддержки подобного варианта), но он может использоваться далеко не во всех случаях; особенно сложно его будет применить к существующим или многорежимным сценариям. Во многих случаях гораздо проще может оказаться добавить вызовы flush в программы командной строки, потоки ввода-вывода которых могут быть связаны с другими программами через сокеты.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011