Некоторые общие мысли:
Модель актера
Хорошо блокировать ожидание ввода от stdin. То же самое относится и к сокету UDP, на который вы намекали.
И stdin, и сокет являются потоками, которые вы, вероятно, разбиваете на отдельные сообщения. Но способ ожидания данных из файловых дескрипторов - это select (), epoll () или аналогичный (в зависимости от вашей платформы; заметьте, Windows дает вам выбор, который работает только с сокетами ...).
В любом случае, я хочу сказать, что, направляясь к использованию select () или epoll (), вы направляетесь к архитектуре Actor Model. Вопрос в том, действительно ли один go для этой целиком, по всему приложению? Я часто обнаруживал, что самый простой ответ - «да».
Это хорошо, потому что тогда ваш поток может выбрать () как для стандартного ввода, так и, скажем, для канала, в который основной поток напишет сообщение "выход". Когда поток обнаруживает, что канал готов к чтению, он читает его, видит сообщение "выход" и полностью закрывается, все аккуратно и аккуратно. Поток будет читать stdin только тогда, когда select () говорит, что есть что прочитать.
Подход "все это дескриптор файла", используемый * nixes, позволяет очень легко охватить практически любой мыслимый источник данных в select () или epoll ().
Linux даже делает сигналы доступными на ФД в эти дни. Гораздо проще считывать сигналы с fd и обрабатывать их синхронно в главном l oop, а не асинхронно в обработчике (который на самом деле мало что может сделать).
Reactor vs Proactor
Архитектура модели актера: Реакторы . У вашего процесса / потоков есть все oop, с чем-то вроде select () в верхней части l oop, и вы читаете ввод с того места, которое готово для чтения, и обрабатываете этот ввод соответственно. (Другие архитектуры, которые являются Reactors, включают в себя последовательные процессы связи, как в Rust и Go -lang, erlang тоже.)
Альтернативой является Proactors , то есть проактивное решение, что делать в продвижение любого входа, появляющегося. Это твердо в стране обратных вызовов, фьючерсов, асинхронных и т. Д. c. Ваш неблокирующий ответ cin выше - proactor.
В наши дни очень много вещей - это Proactor; Windows есть, Boost ASIO есть (потому что Windows есть) и так далее.
Старайтесь не смешивать Proactor и Reactor
Дело в том, что можно попасть в ужасную ситуацию, пытаясь смешать архитектуру Reactor с архитектурой Proactor. Это вообще ужасно. Поэтому я советую выбрать один (реактор или проактор), придерживайтесь его и не пытайтесь смешать их вместе в какой-либо значительной степени.
Это означает, что нужно очень тщательно обдумать заранее (например, придется ли мне делать это на Windows, или мне когда-либо придется переносить его на Windows и т. Д. c).
Мои предпочтения
Обычно я предпочитаю Reactor. В Proactor нужно инициировать такие вещи, как чтение через сокет, заранее зная, нужно ли что-либо читать, что приводит к той проблеме, которую вы пытаетесь избежать (функция чтения ввода, которая блокируется и никогда не будет разблокирована в определенных обстоятельствах, требующих грязного отключения).
В Reactor приложение никогда не будет вызывать функцию чтения, пока не узнает, что есть что-то прочитать, а состояние потока таково, что должно произойти чтение (например, команда "quit" не получена) .
Неэквивалентность Pro / Reactor
Другой аспект заключается в том, что возможно реализовать поведение Proactor поверх инфраструктуры Reactor, но в действительности это невозможно.
Хорошие примеры этого включают ZMQ. ZMQ - это в основном архитектура модели Actor, Reactor, построенный на основе zmq_poll (). Для ZMQ имеется множество Windows креплений, которые представляют фасад в стиле Proactor.
ZMQ может работать только на Windows для сокетов (потому что Windows дает вам select () только для сокетов), но не будет работать на Windows каналах (нет выбора () для каналов). В Unix каналы поддерживаются транспортом ip c: //. Отсутствие каналов не так уж плохо в процессе на Windows, так как транспорт inpro c: // работает.
Аналогично, Boost ASIO является проактором просто из-за сложности реализации реактора на Windows, в то же время охватывая полный набор IP C, таких как каналы, сокеты и последовательные порты на Windows (что-то, что ZMQ выбрал не делать). На * nix, Boost ASIO представляет фронтальный процессор, но под капотом он реализован с использованием epoll () ...
ZMQ
Говоря о ZMQ, если вы работая над * nix, я настоятельно рекомендую использовать ZMQ в качестве библиотеки IP C. Он делает фантастическую c работу по созданию таких вещей, как ip c, по-настоящему простыми в использовании сокетами, и вы можете легко интегрировать такие вещи, как ожидание от обычных файловых дескрипторов (например, stdin), в вызов zmq_poll (). Более того, многие другие фреймворки, например, GUI фреймворки, позволяют вам использовать fd в качестве входных данных в их собственных циклах событий, и это может включать в себя fd «что-то случилось», которое дает вам ZMQ. Таким образом, относительно просто включить обработку ZMQ, встроенную в приложение gui.
Если вы используете его на Windows, будет невозможно интегрировать его с stdin в настоящий стиль реактора.
Общая история
Когда ребята-cygwin пришли к реализации select () для своей библиотеки на Windows, они столкнулись с ужасом, который заключался в невозможности получить правильная блокировка select (), которая может ожидать сокеты, последовательные порты, stdin, pipe и т. д. c. В конце концов, они реализовали это, имея поток на дескриптор файла, не относящийся к сокету, вращаясь в al oop, проверяя дескриптор устройства Windows, чтобы увидеть, не появилось ли что-нибудь. Это было крайне неэффективно.
Boost ASIO вышел proactor специально из-за желания заставить Boost работать на Windows.
Windows (кроме сокетов) с самого начала был нереформированным проактором, вероятно, вплоть до ядра и драйверов устройств. Однако в первой версии WSL (в настоящее время WSL довольно популярен) реализован системный вызов Linux shim; т. е. программа Linux, выполняющая вызов select (), в конечном итоге вызовет ядро NT для эквивалентной функции.
Это означает, что каналы, по крайней мере внутри WSL 1.0, будут работать в select (), но это будет вызов ядра NT, реализующий его. Это означает, что некоторые элементы реактора куда-то попали в Windows, но не были выставлены на уровне Win32 / C, C ++.
Будучи решительно проактором, Windows очень напоминает древние Unixes, которые также не могли делать select ().