Ich wundere mich über das Verhalten von Socket bei blockierenden und nicht blockierenden Aktionen. Was passiert mit Threads, die auf Sockets blockieren, wenn sich der Socket-Blocking-Modus ändert? Hier ist das Szenario; thread1 (T1) erstellt einen UDP-Socket und
%Vor%T1 wartet (schläft) für Empfang
%Vor%und thread2 (T2) ändern den Socket-Modus auf nicht blockierend, bevor Socket irgendwelche Daten empfängt
%Vor%Was passiert mit T1? Wird es signalisiert / geweckt, weil der Socket nicht mehr blockiert?
Das Verhalten ist buchstäblich nicht spezifiziert: fcntl
ist nicht erforderlich, um Threads freizugeben.
Linux setzt nur die Flagge in der Dateibeschreibung struct file
und gibt zurück, ohne blockierte Threads zu entsperren.
Ein Thread bereits blockiert in recv
kann so geplant werden, dass er nur ausgeführt wird, wenn:
FIN
/ RST
, Socket-Lesezeitlimit, TCP-Keepalive-Fehler, der Dateideskriptor ist close
d von einem anderen Thread); SA_RESTART
; pthread_cancel
led. Die Tatsache, dass Sie versuchen, die Flags eines Dateideskriptors eines anderen Threads zu ändern, deutet darauf hin, dass Ihr Design einer Überprüfung bedarf. Idealerweise dürfen Threads keine Daten gemeinsam nutzen und nicht den Zustand des jeweils anderen anstoßen. Stattdessen sollten sie die Nachrichtenübergabe verwenden, um miteinander zu kommunizieren.
Der Code oben (Sorry, konnte es nicht kürzer machen). Blockiert den Thread in recv, wartet ein wenig und setzt anschließend den Dateideskriptor nicht blockierend. Wie erwartet passiert nichts. Aber dann habe ich eine Wendung hinzugefügt. Der Empfänger-Thread hat einen Signal-Handler, der jede Sekunde mit SA_RESTART
aufwacht. Natürlich, da wir SA_RESTART
recv
nicht aufwachen haben.
Unter OSX wird sich dies "schlecht benehmen", wenn wir irgendein Verhalten für richtig halten. Ich bin mir ziemlich sicher, dass sich das auf allen BSDs genauso verhält. Gleiches gilt für Linux. In der Tat, die Art und Weise, wie SA_RESTART
normalerweise implementiert wird, bin ich mir ziemlich sicher, dass dies sich ziemlich überall "schlecht benehmen" wird.
Lustig. Dies bedeutet natürlich nicht, dass der obige Code für irgendetwas nützlich ist, aber es ist eine interessante Kuriosität. Um Ihre Frage zu beantworten, ist dies nicht spezifiziert, aber in den meisten Fällen wird es nicht aufwachen, es sei denn, es wird. Bitte mach keine komischen Dinge wie diese.
Ich denke POSIX-Spezifikation ist ziemlich klar darüber:
Wenn keine Nachrichten am Socket verfügbar sind und
O_NONBLOCK
nicht auf den Dateideskriptor des Sockets gesetzt ist, blockiertrecv()
, bis eine Nachricht eintrifft. Wenn keine Nachrichten am Socket verfügbar sind undO_NONBLOCK
auf den Dateideskriptor des Sockets gesetzt ist, wirdrecv()
fehlschlagen und errno auf[EAGAIN]
oder[EWOULDBLOCK]
setzen.
Sie rufen recv
auf, wenn O_NONBLOCK
noch nicht gesetzt ist, bis die Nachricht eintrifft (erst wenn sich der Modus ändert).
Nicht blockierte Thread (blockierte noch nicht, weil er gelesen wurde, als er sich im Blocking-Modus befand) verhält sich so, als wäre er immer ein nicht blockierender Modus.
Lesen recv (2) Handbuchseite
Wenn keine Nachrichten am Socket verfügbar sind und der Socket nicht blockiert ist, wird der Wert -1 zurückgegeben und der externe Wert Variable errno wird auf EAGAIN oder EWOULDBLOCK gesetzt.
Blockierte Threads (blockiert während des Lesens im Blocking-Modus), bevor Sie in nicht blockierende Threads wechseln. Wie von @Maxim gezeigt, der den Code der Funktion teilt, die keine Threads aufweckt, werden die blockierten Threads erst nach einem Schreibvorgang (Daten sind verfügbar) aktiviert.
Tags und Links c multithreading sockets nonblocking