Есть ли вариант использования неблокирующего приема, когда у меня есть потоки?

Я знаю, что неблокирующий прием не так часто используется при передаче сообщений, но все же какая-то интуиция подсказывает мне, что это необходимо. Возьмем, к примеру, приложения, управляемые событиями с графическим интерфейсом, вам нужно каким-то образом дождаться сообщения неблокирующим образом, чтобы ваша программа могла выполнять некоторые вычисления. Один из способов решить эту проблему — создать специальный поток с очередью сообщений. Есть ли какой-то вариант использования, когда вам действительно понадобится неблокирующий прием, даже если у вас есть потоки?


person Gabriel Ščerbák    schedule 13.04.2010    source источник
comment
Плачетка вымысла блбости :). (Извините за неанглийский ответ, это все равно не по теме.)   -  person michalburger1    schedule 23.04.2010
comment
@Bus: по-чешски Ткань изобретает дерьмо. Я не совсем понимаю.   -  person BalusC    schedule 23.04.2010


Ответы (6)


Потоки работают иначе, чем неблокирующие асинхронные операции, хотя обычно вы можете добиться того же эффекта, используя потоки, выполняющие синхронные операции. Однако, в конце концов, все сводится к тому, как справляться с делами более эффективно.

Потоки — это ограниченные ресурсы, и их следует использовать для обработки длительных активных операций. Если у вас есть что-то, что не очень активно работает, но вам нужно какое-то время бездействовать для результата (подумайте о какой-то операции ввода-вывода по сети, например, вызове веб-сервисов или серверов баз данных), то лучше использовать предоставленный асинхронная альтернатива вместо того, чтобы тратить потоки без необходимости, помещая синхронный вызов в другой поток.

Вы можете хорошо ознакомиться с этой проблемой здесь для большего понимания.

person Amry    schedule 23.04.2010
comment
Как раз о том, что я собирался ответить. Я бы также упомянул select(), но это не так универсально, как хотелось бы. Если можно обрабатывать 500 подключений на поток, один процесс может обрабатывать 250 000 подключений. - person Joshua; 23.04.2010
comment
Да, и если бы один поток мог обрабатывать 1 000 000 соединений, вы могли бы справиться с 1 000 000 000 соединений! Но да, конечно, я согласен, если вы можете мультиплексировать один поток через кучу подключений (используя select, poll, kqueue, WaitForMultipleObjects... что угодно), вы должны это сделать, он масштабируется намного лучше. - person MK.; 23.04.2010
comment
На платформах Windows вы можете использовать порты завершения ввода-вывода и асинхронный ввод-вывод для обработки десятков тысяч активных подключений и ожидающих запросов ввода-вывода с очень небольшим (мы говорим 2-4) потоками... - person Len Holgate; 23.04.2010
comment
Вы сталкиваетесь с другими проблемами задолго до того, как доберетесь до мифического миллиарда. Во-первых, операционная система, скорее всего, выйдет из строя на нескольких тысячах одновременно... - person Donal Fellows; 23.04.2010
comment
спасибо за ваш ответ, но я хотел бы услышать некоторые аргументы, игнорирующие тот факт, что количество потоков ограничено, потому что это может легко измениться. Посмотрите на мой собственный ответ, пожалуйста, и прокомментируйте, спасибо! Статью скоро посмотрю.... - person Gabriel Ščerbák; 27.04.2010

Один поток на соединение часто не является хорошей идеей (тратится память, не все ОС очень хороши с огромным количеством потоков и т. д.).

Как прервать блокирующий входящий вызов? Например, в Linux (и, возможно, в некоторых других ОС POSIX) pthreads + signal = авария. С неблокирующим получением вы можете мультиплексировать ожидание в принимающем сокете и каком-то сокете IPC, используемом для связи между вашими потоками. Также относительно легко сопоставляется с миром Windows.

Если вам нужно заменить обычный сокет чем-то более сложным (например, OpenSSL), полагаясь на поведение блокировки, у вас могут возникнуть проблемы. OpenSSL, например, может зайти в тупик на блокирующем сокете, потому что протокол SSL имеет сценарии инверсии отправитель/получатель, в которых получение не может быть продолжено до того, как будет выполнена какая-либо отправка.

Мой опыт таков: «если есть сомнения, используйте неблокирующие сокеты».

person MK.    schedule 23.04.2010
comment
Хм, так вы видите проблему в потенциальном тупике, это интересно. Вы также упоминаете синхронизацию и связь между потоками (неявно). Можете ли вы привести какой-нибудь минималистичный пример, где реализовать связь между потоками сложнее, чем эффективный вызов неблокирующих операций? Это то, на что я намекал в своем собственном ответе на мой вопрос... - person Gabriel Ščerbák; 27.04.2010
comment
Вот ветка, обсуждающая взаимоблокировку в OpenSSL, вызванную блокировкой сокетов: marc.info/?t= 114988489900003&r=3&w=2 Я не уверен, что понимаю вторую часть вашего вопроса на 100%, но в основном у меня есть пул потоков, обрабатывающий несколько соединений; вы не можете реализовать это элегантно без неблокирующих сокетов. Все становится еще красивее (но несколько сложнее для понимания) с асинхронными сокетами (завершениями ввода-вывода), но неблокирующие сокеты, управляемые событиями, обеспечивают простоту и эффективность. - person MK.; 27.04.2010
comment
Я настоятельно рекомендую вам проверить mailinator.com/tymaPaulMultithreaded.pdf - person Peter Štibraný; 29.04.2010
comment
Я не могу эффективно читать слайды PowerPoint, но для его теста нет исходного кода, и я подозреваю, что он выполнял 1 поток для всех соединений в тесте NIO, что не лучший способ сделать что-то, у вас должен быть один дескриптор потока 5-60 подключений в зависимости от кучи вещей. Также мне не ясно, что вещи чисто экстраполируются с Java на все остальное. - person MK.; 29.04.2010

Из-за блокировки ввода-вывода на многих платформах сложно заставить ваше приложение выполнять упорядоченное завершение работы в условиях медленных, зависших или отключенных клиентов/служб.

С неблокирующим вводом-выводом вы можете завершить операцию в полете, как только системный вызов вернется, то есть немедленно. Если ваш код написан с учетом преждевременного завершения — что сравнительно просто с неблокирующим вводом-выводом — это может позволить вам изящно очистить сохраненное состояние.

person Jason    schedule 28.04.2010

Я не могу придумать ни одного, но иногда неблокирующие API разрабатываются таким образом, чтобы сделать их более простыми/более интуитивно понятными в использовании, чем явно многопоточная реализация.

person 500 - Internal Server Error    schedule 22.04.2010
comment
Я думаю, что в настоящее время существует больше параллельных моделей, которые, как правило, проще в использовании, поэтому я не думаю, что это очень правильно. - person Gabriel Ščerbák; 27.04.2010

Вот реальная ситуация, с которой я недавно столкнулся. Раньше у меня был скрипт, который запускался каждый час под управлением crontab, но иногда пользователи подключались к машине и запускали скрипт вручную. У этого были некоторые проблемы, например, одновременное выполнение crontab и пользователем могло вызвать проблемы, и иногда пользователи входили в систему как root - я знаю, плохой шаблон, не под моим контролем - и запускали скрипт с неправильными разрешениями. Поэтому мы решили запустить подпрограмму в качестве демона с соответствующими разрешениями, и команда, которую использовали пользователи для запуска, теперь просто запускала демона.

Таким образом, эта выполняемая пользователем команда в основном делает две вещи: запускает демон и ждет, пока он завершит задачу. Но для этого также требовался тайм-аут и сохранение журналов демона для пользователя во время ожидания.

Если я понимаю ситуацию, которую вы предложили, у меня был случай, который вы хотите: мне нужно было продолжать слушать демона, продолжая при этом независимо взаимодействовать с пользователем. Решением было асинхронное чтение.

К счастью для меня, я не думал об использовании потоков. Я бы, наверное, так и подумал, если бы программировал на Java, но это был код на Python.

person lfagundes    schedule 28.04.2010
comment
Хороший пример неблокирующего получения, однако я не понял, чем это лучше, чем потоки, по вашему мнению. Тем не менее, я дал вам голос, я ценю опыт и усилия, приложенные при ответе. - person Gabriel Ščerbák; 29.04.2010
comment
Для меня лучше то, что решает проблему как можно проще :-). Как я уже сказал, если бы я использовал java, я бы, вероятно, перешел к потокам, потому что поддержка потоков java очень хороша, а синхронизация проста, в то время как в python эта поддержка не так хороша. Я думаю, что у каждого решения есть свои плюсы и минусы, и когда вы говорите, что одно из них лучше, вы должны иметь параметры, которые наиболее важны в вашем случае. До сих пор мой опыт работы с потоками был хорош для интенсивной многозадачности с общей памятью в C++ и java, но я получил гораздо лучшие результаты с fork в perl и python. - person lfagundes; 29.04.2010

Я хочу сказать, что когда мы считаем потоки и обмен сообщениями совершенными, реальный компромисс заключается в написании планировщика для планирования неблокирующих операций приема и написании кода синхронизации для потоков с общим состоянием (блокировки и т. д.). Я бы сказал, что и то, и другое может быть когда-то легким, а когда-то трудным. Таким образом, вариант использования будет иметь место, когда нужно получить много асинхронных сообщений и когда есть много данных, с которыми нужно работать на основе сообщений. Это было бы довольно легко в одном потоке с использованием неблокирующего приема и потребовало бы большой синхронизации со многими потоками и общим состоянием.... Я также думаю о каком-то примере из реальной жизни, я включу его, вероятно, позже.

person Gabriel Ščerbák    schedule 23.04.2010