Вызовите recv () для одного и того же блокирующего сокета из двух потоков - PullRequest
10 голосов
/ 19 марта 2009

Что произойдет, если у меня будет один сокет, s, на нем в настоящее время нет доступных данных, это блокирующий сокет, и я вызываю recv на нем из двух потоков одновременно? Получит ли один из потоков данные? Оба получат это? Вернется ли второй вызов recv с ошибкой?

Ответы [ 4 ]

10 голосов
/ 19 марта 2009

Один поток получит его, и невозможно определить, какой именно.

Это не похоже на разумный дизайн. Есть ли причина, по которой вам нужны два потока, вызывающие recv() в одном сокете?

3 голосов
/ 19 марта 2009

Я не могу найти ссылку на это, но вот мое понимание:

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

Предположим, что поток A вызывает recv () для сокета, который принимает потоковые данные TCP с высокой скоростью. Если recv () должен быть атомарным вызовом, тогда поток A может заблокировать выполнение всех других потоков, потому что он должен работать непрерывно, чтобы извлекать все данные (до тех пор, пока его буфер не заполнится) хорошо. Следовательно, я не предполагаю, что recv () неуязвим для переключения контекста.

И наоборот, предположим, что поток A выполняет блокирующий вызов для recv () на TCP-сокете, и данные поступают медленно. Следовательно, вызов recv () возвращается с errno, установленным в EAGAIN.

В любом из этих случаев предположим, что поток B вызывает recv () для того же сокета, в то время как поток A все еще получает данные. Когда поток A прекращает получать данные, передаваемые ему, чтобы поток B мог начать получать данные? Я не знаю реализации Unix, которая будет пытаться вспомнить, что поток A находился в середине операции над сокетом; вместо этого, приложение (потоки A и B) должно договориться об использовании его.

Обычно лучше спроектировать приложение так, чтобы только один из потоков вызывал recv () для одного сокета.

3 голосов
/ 19 марта 2009

Реализации сокетов должны быть поточно-ориентированными, поэтому ровно один поток должен получать данные, когда они становятся доступными. Другой вызов должен просто заблокироваться.

2 голосов
/ 28 марта 2009

Из справочной страницы на recv

recv () для сокета SOCK_STREAM возвращает как можно больше доступной информации так как размер поставляемого буфера может держать.

Предположим, вы используете TCP, так как он не был указан в вопросе. Предположим, что у вас есть поток A и поток B, которые блокируют функцию recv () для сокета s. Как только s получит некоторые данные, он разблокирует один из потоков, скажем, A, и вернет данные. Возвращенные данные будут иметь произвольный размер, насколько нам известно. Поток A проверяет полученные данные и решает, имеет ли оно полное «сообщение», где сообщение является концепцией прикладного уровня.

Поток A решает, что не имеет полного сообщения, поэтому он снова вызывает recv (). НО в это время B уже блокировал тот же сокет и получил остальную часть «сообщения», предназначенного для потока A. Здесь я использую намеренно свободно.

Теперь поток A и поток B имеют неполное сообщение и, в зависимости от того, как написан код, будут выбрасывать данные как недействительные или вызывать странные и тонкие ошибки.

Я бы хотел сказать, что не знал этого по опыту.

Так что, хотя сам recv () технически безопасен для потоков, плохая идея иметь два потока, вызывающих его одновременно, если вы используете его для TCP.

Насколько я знаю, это абсолютно безопасно, когда вы используете UDP.

Надеюсь, это поможет.

...