2015-01-10 6 views
0

У меня нет большого опыта работы с асинхронными вводами-выводами на сокетах. Недавно мне пришлось внедрить TCP-клиент, который будет подключаться к серверу в отдельном потоке и ждать входных данных, а это неблокируемо, чтобы поток можно было немедленно завершить, просто установив некоторый флаг, который можно было бы использовать из потока управления. Моим решением было установить неблокирование сокета и реализовать цикл, который вызывал recv снова и снова, если errno был E_WOULDBLOCK и вышел из цикла, когда флаг переменной запустил больше не был установлен. Это было так:Неблокирующий выпуск ввода-вывода

while(run) { 
    if(-1 == recv(...)) 
    { 
     if(errno == E_WOULDBLOCK) 
      continue; 
     else 
      break; 
    } 
    else 
    { 
     process_data(...); 
    } 
} 

Вопрос в том, насколько хорошо это решение с точки зрения использования ЦП? Будет ли использование select/poll/epoll более эффективным?

И еще один вопрос: есть ли способ сделать заблокирован выбрать, чтения или connnect вызов возврата непосредственно из другого потока?

+0

Какой язык (C, C++) !? – user1766169

+0

Язык: C – olegst

+0

Лучше быть: 'if ((errno == E_WOULDBLOCK) || (errno == EAGAIN))' – alk

ответ

2

Вопрос в том, насколько хорошо это решение с точки зрения использования ЦП? Будет ли эффективнее с использованием select/poll/epoll?

ужасно неэффективен. Вероятно, это худший способ обработки нескольких соединений. Учитывайте, что каждый recv является системным вызовом: для этого требуется контекстный переключатель. Переключатель контекста не дорог, но вычисление контекстного переключателя в высокочастотном контуре, скорее всего, приведет к использованию ЦП. Кроме того, если вы должны были спать между разговором и другим, чтобы «смягчить цикл», вы в конечном итоге заплатите за задержку между получением и обработкой данных; чем больше у вас связей, тем выше будет ощущаться.

select в основном сообщает ядру: «Я хочу, чтобы эти наборы fds отслеживались вами, а также передавались, как только что-то случалось». Ваш процесс войдет в состояние ожидания и станет готовым при доставке события. Между тем, ЦП может обслуживать другие процессы.

Вы можете использовать блокирующий мультиплексор и обрабатывать каждую работу в отдельном потоке (возможно, из пула потоков), что очень эффективно. Но это зависит от того, что вы делаете.

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

Ну, да. Но что вы хотите иметь? Как сказал @alk, вы можете отправить им сигнал. Кроме того, для recv и connect вы можете использовать select/poll или любой другой мультиплексор с таймаутом; это может быть сложнее запрограммировать.

2

Насколько хорошо это решение с точки зрения использования ЦП?

Занятое ожидание очень неэффективно.

Будет ли использование select/poll/epoll быть более эффективным?

Да.

есть ли способ сделать заблокирован select, read или connnect возвращение сразу

Да, отправить блокированный поток сигнала. Возвращаемая функция возвращает -1, а errno - EINTR. При настройке обработчика сигнала заботится о том, как обрабатывается SA_RESTART с помощью реализации вашего компилятора.

+0

Использование сигнала для пробуждения другого потока может быть небезопасным, если вы не знаете состояния, в котором находится другой поток. Если другой поток происходит с/not/быть заблокирован на 'select', сигнал может разорвать все, что угодно на самом деле происходит. – user3553031

+0

On может очень хорошо настроить обработчик сигнала перед входом в секцию, которая должна блокировать и отключать обработчик при выходе из этого раздела. @ user3553031 – alk

0

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

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

0

Предполагая, что вы программируете на C и имеете достойную версию ядра, pselect - это путь.

Ожидание ввода и сигналов одновременно является хорошо известной проблемой, и есть много хороших дискуссий о связанных с ней проблемах и решениях. Вы можете найти this discussion хорошее начало.

Смежные вопросы