Вызовите recv() в одном и том же блокирующем сокете из двух потоков

StackOverflow https://stackoverflow.com/questions/660422

  •  20-08-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

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

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

Другие советы

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

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

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

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

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

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

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

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

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

Давайте предположим, что вы используете TCP, поскольку он не был указан в вопросе.Итак, предположим, что у вас есть поток A и поток B, оба блокирующие recv() для сокета s.Как только s получит некоторые данные, которые необходимо получить, он разблокирует один из потоков, скажем, A, и вернет данные.Насколько нам известно, возвращаемые данные будут иметь некоторый случайный размер.Поток A проверяет полученные данные и решает, содержит ли он полное "сообщение", где сообщение - это концепция уровня приложения.

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

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

Хотел бы я сказать, что не знал этого по собственному опыту.

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

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

Я надеюсь, что это поможет.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top