Czy równoległe wywołania send / recv w tym samym gnieździe są prawidłowe?

127
  1. Czy możemy wywołać send z jednego wątku i recv z innego na tym samym gnieździe?
  2. Czy możemy wywołać wiele wysyłek równolegle z różnych wątków w tym samym gnieździe?

Wiem, że dobry projekt powinien tego unikać, ale nie jestem pewien, jak zachowają się te systemowe API. Nie mogę znaleźć dobrej dokumentacji również do tego samego.

Pomocne będą wszelkie wskazówki dotyczące kierunku.

Sójka
źródło
3
dlaczego twierdzisz, że takie postępowanie jest złą praktyką? Dla mnie wygląda dobrze, ponieważ słuchasz i odbierasz w różnych wątkach.
TheMathNoob

Odpowiedzi:

92

POSIX definiuje send / recv jako operacje atomowe, więc zakładając, że mówisz o POSIX send / recv, to tak, możesz wywoływać je jednocześnie z wielu wątków i wszystko będzie działać.

Nie musi to koniecznie oznaczać, że będą wykonywane równolegle - w przypadku wielu wysyłek druga prawdopodobnie będzie blokowana do zakończenia pierwszej. Prawdopodobnie nie zauważysz tak bardzo, ponieważ wysyłanie kończy się, gdy umieści swoje dane w buforze gniazda.

Jeśli używasz gniazd SOCK_STREAM, próba wykonania czynności równoległych jest mniej przydatna, ponieważ send / recv może wysłać lub odebrać tylko część wiadomości, co oznacza, że ​​rzeczy mogą się rozdzielić.

Blokowanie wysyłania / odbierania na gniazdach SOCK_STREAM blokuje tylko do momentu wysłania lub odebrania co najmniej 1 bajtu, więc różnica między blokowaniem a nieblokowaniem nie jest użyteczna.

Chris Dodd
źródło
1
@Joao: Gniazda SOCK_DGRAM są udokumentowane jako „zachowywanie granic wiadomości”, co nie jest zbyt jasne. Patrząc na źródła jądra Linuksa, możesz przynajmniej zobaczyć, że każde send i recv zajmuje się pojedynczym pakietem atomowo (przynajmniej w przypadku udp).
Chris Dodd
2
@Kedar: nie wiem, co masz na myśli. A sendpowraca, gdy tylko dane zostaną umieszczone w buforze wysyłania, a dane są przesyłane przez stos sieciowy i asynchronicznie do sieci. Więc jeśli masz jeden wątek wysyłający i jeden odbierający, jest całkowicie możliwe (nawet prawdopodobne) wysłanie przez wątek wysyłający wielu pakietów, zanim wątek odbierający otrzyma pierwszy pakiet. Jest całkowicie asynchroniczny i nie symultaniczny.
Chris Dodd,
6
@ChrisDodd, czy możesz podać łącze do „POSIX definiuje wysyłanie / odbieranie jako operacje atomowe”?
suitianshi
2
@suitianshi: Standardowy dokument POSIX 1003.1c zawiera listę wszystkich funkcji w 1003.1, które są reentrantem (bezpieczne wywołanie z wątków), a które nie. Niestety, nie znam bezpłatnej kopii internetowej dostępnej w dowolnym miejscu.
Chris Dodd
2
@ChrisDodd Znalazłem kopię na unix-systems.org/version4 i widzę listę tabeli interfejsów systemu w rozdziale 7.1, ale nie widzę, gdzie zawiera listę funkcji jako operacji atomowych. Nie wątpię, ale czy możesz udostępnić / edytować swoją odpowiedź, aby uzasadnić swój punkt widzenia w dokumencie?
user153882
17

Deskryptor gniazda należy do procesu, a nie do określonego wątku. W związku z tym możliwe jest wysyłanie / odbieranie do / z tego samego gniazda w różnych wątkach, system operacyjny zajmie się synchronizacją.

Jeśli jednak kolejność wysyłania / odbierania jest znacząca semantycznie, Ty sam (odpowiednio Twój kod) musisz zapewnić odpowiednią sekwencję między operacjami w różnych wątkach - jak zawsze w przypadku wątków.

Adrian Willenbücher
źródło
4

Nie widzę, jak równoległe odbieranie mogłoby cokolwiek osiągnąć. Jeśli masz wiadomość 3-bajtową, jeden wątek może otrzymać pierwsze 2 bajty, a drugi ostatni bajt, ale nie możesz stwierdzić, który jest który. Jeśli twoje wiadomości nie mają długości tylko bajtu, nie ma możliwości, aby cokolwiek działało niezawodnie przy odbieraniu wielu wątków.

Wiele wysyłek może zadziałać, jeśli wysłałeś całą wiadomość w jednym połączeniu, ale nie jestem pewien. Możliwe, że jeden mógłby nadpisać inny. Z pewnością nie przyniosłoby to żadnych korzyści w zakresie wydajności.

Jeśli trzeba wysłać wiele wątków, należy zaimplementować zsynchronizowaną kolejkę wiadomości. Miej jeden wątek, który wykonuje faktyczne wysyłanie, który czyta wiadomości z kolejki, a pozostałe wątki kolejkują całe wiadomości. To samo działałoby w przypadku odbierania, ale wątek odbioru musiałby znać format wiadomości, aby mógł je prawidłowo deserializować.

noah
źródło
9
Jeśli używasz gniazd SOCK_DGRAM, każde recv otrzyma pojedynczy datagram; nigdy nie zostanie podzielony na recvs
Chris Dodd,
2
@noah, zgadzam się, że równoległe recvs nie mogą nic osiągnąć. Dlatego o to nie zapytałem. Moje pytanie brzmi: wyślij / odbierz równolegle, a następnie równolegle wysyłaj wiele razy. Twoja odpowiedź daje wgląd w równoległe wysyłki. Dzięki za to samo.
Jay
1
@ Chris to dobra uwaga. Zakładałem TCP. @Jay Możesz wyjaśnić pytanie „Czy możemy wywołać równolegle wysyłanie / odbieranie” brzmi tak, jakbyś chciał odbierać równolegle.
noah