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

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

Ответ 1

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

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

Ответ 2

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

Ответ 3

Я не могу найти ссылку для этого, но здесь мое понимание:

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

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

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

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

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

Ответ 4

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

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

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

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

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

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

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

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

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