Я знаю, что неблокирующий прием не так часто используется при передаче сообщений, но все же какая-то интуиция подсказывает мне, что это необходимо. Возьмем, к примеру, приложения, управляемые событиями с графическим интерфейсом, вам нужно каким-то образом дождаться сообщения неблокирующим образом, чтобы ваша программа могла выполнять некоторые вычисления. Один из способов решить эту проблему — создать специальный поток с очередью сообщений. Есть ли какой-то вариант использования, когда вам действительно понадобится неблокирующий прием, даже если у вас есть потоки?
Есть ли вариант использования неблокирующего приема, когда у меня есть потоки?
Ответы (6)
Потоки работают иначе, чем неблокирующие асинхронные операции, хотя обычно вы можете добиться того же эффекта, используя потоки, выполняющие синхронные операции. Однако, в конце концов, все сводится к тому, как справляться с делами более эффективно.
Потоки — это ограниченные ресурсы, и их следует использовать для обработки длительных активных операций. Если у вас есть что-то, что не очень активно работает, но вам нужно какое-то время бездействовать для результата (подумайте о какой-то операции ввода-вывода по сети, например, вызове веб-сервисов или серверов баз данных), то лучше использовать предоставленный асинхронная альтернатива вместо того, чтобы тратить потоки без необходимости, помещая синхронный вызов в другой поток.
Вы можете хорошо ознакомиться с этой проблемой здесь для большего понимания.
Один поток на соединение часто не является хорошей идеей (тратится память, не все ОС очень хороши с огромным количеством потоков и т. д.).
Как прервать блокирующий входящий вызов? Например, в Linux (и, возможно, в некоторых других ОС POSIX) pthreads + signal = авария. С неблокирующим получением вы можете мультиплексировать ожидание в принимающем сокете и каком-то сокете IPC, используемом для связи между вашими потоками. Также относительно легко сопоставляется с миром Windows.
Если вам нужно заменить обычный сокет чем-то более сложным (например, OpenSSL), полагаясь на поведение блокировки, у вас могут возникнуть проблемы. OpenSSL, например, может зайти в тупик на блокирующем сокете, потому что протокол SSL имеет сценарии инверсии отправитель/получатель, в которых получение не может быть продолжено до того, как будет выполнена какая-либо отправка.
Мой опыт таков: «если есть сомнения, используйте неблокирующие сокеты».
Из-за блокировки ввода-вывода на многих платформах сложно заставить ваше приложение выполнять упорядоченное завершение работы в условиях медленных, зависших или отключенных клиентов/служб.
С неблокирующим вводом-выводом вы можете завершить операцию в полете, как только системный вызов вернется, то есть немедленно. Если ваш код написан с учетом преждевременного завершения — что сравнительно просто с неблокирующим вводом-выводом — это может позволить вам изящно очистить сохраненное состояние.
Я не могу придумать ни одного, но иногда неблокирующие API разрабатываются таким образом, чтобы сделать их более простыми/более интуитивно понятными в использовании, чем явно многопоточная реализация.
Вот реальная ситуация, с которой я недавно столкнулся. Раньше у меня был скрипт, который запускался каждый час под управлением crontab, но иногда пользователи подключались к машине и запускали скрипт вручную. У этого были некоторые проблемы, например, одновременное выполнение crontab и пользователем могло вызвать проблемы, и иногда пользователи входили в систему как root - я знаю, плохой шаблон, не под моим контролем - и запускали скрипт с неправильными разрешениями. Поэтому мы решили запустить подпрограмму в качестве демона с соответствующими разрешениями, и команда, которую использовали пользователи для запуска, теперь просто запускала демона.
Таким образом, эта выполняемая пользователем команда в основном делает две вещи: запускает демон и ждет, пока он завершит задачу. Но для этого также требовался тайм-аут и сохранение журналов демона для пользователя во время ожидания.
Если я понимаю ситуацию, которую вы предложили, у меня был случай, который вы хотите: мне нужно было продолжать слушать демона, продолжая при этом независимо взаимодействовать с пользователем. Решением было асинхронное чтение.
К счастью для меня, я не думал об использовании потоков. Я бы, наверное, так и подумал, если бы программировал на Java, но это был код на Python.
Я хочу сказать, что когда мы считаем потоки и обмен сообщениями совершенными, реальный компромисс заключается в написании планировщика для планирования неблокирующих операций приема и написании кода синхронизации для потоков с общим состоянием (блокировки и т. д.). Я бы сказал, что и то, и другое может быть когда-то легким, а когда-то трудным. Таким образом, вариант использования будет иметь место, когда нужно получить много асинхронных сообщений и когда есть много данных, с которыми нужно работать на основе сообщений. Это было бы довольно легко в одном потоке с использованием неблокирующего приема и потребовало бы большой синхронизации со многими потоками и общим состоянием.... Я также думаю о каком-то примере из реальной жизни, я включу его, вероятно, позже.