Socket.BeginXxx und ThreadPool
-
*push*
-
Dieser Thread wurde von Moderator/in Jochen Kalmbach aus dem Forum C++/CLI mit .NET in das Forum C# und .NET verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
So wie ich das verstanden habe ist der initiierende Thread ja nicht der IOCP Thread (#1) der den BeginAccept-Callback aufruft sondern derjenige (#2) der den BeginReceive-Callback ruft. D.h. das #1 sich dann problemlos beenden kann.
-
Hä?
Der "initiator" ist natürlich der der BeginXxx aufruft.
-
Es geht aber um den Thread der EndXxx aufruft. Die I/O Operation findet ja sinnigerweise nicht im Aufrufenden Thread statt, oder?
-
Es geht immer nur um die Threads die BeginXxx aufrufen. EndXxx "initiiert" ja keinen IO, sondern holt sich bloss das Ergebnis fertiger IOs.
Beispiel:using System; using System.Collections.Generic; using System.Text; using System.Net.Sockets; using System.Diagnostics; namespace WindowsApplication1 { class Foo { public Foo(Socket s) { m_socket = s; } public void Start() { if (m_running) throw new InvalidOperationException("blah"); // das BeginReceive hier ist kein Problem, das kommt üblicherweise aus dem Main-Thread, // und den lassen wir ja leben m_socket.BeginReceive(m_buffer, 0, m_buffer.Length, SocketFlags.None, ReceiveCallback, null); m_running = true; } private void ReceiveCallback(IAsyncResult ar) { try { Debug.Assert(m_running); // ergebnis des soeben abgeschlossenen IOs holen int received = m_socket.EndReceive(ar); ProcessInput(m_buffer, 0, received); if (IsDone()) { m_socket.Close(); m_running = false; } else { // DIESES BeginReceive hier ist das (möglicherweise) problematische, denn // diese Callback-Funktion wird aus einem Thread des .NET Thread-Pools aufgerufen. m_socket.BeginReceive(m_buffer, 0, m_buffer.Length, SocketFlags.None, ReceiveCallback, null); } } catch (Exception) { // handle error // ... m_socket.Close(); m_running = false; } } private void ProcessInput(byte[] array, int offset, int length) { // ... } private bool IsDone() { return whatever; } private bool m_running = false; private Socket m_socket = null; private byte[] m_buffer = new byte[1024]; } }
Vielleicht sorgt das .NET Framework auf magische Art und Weise dafür dass es geht. Wenn dann hab' ich den diesbezüglichen Code nicht erkannt, denn durchgesteppt hab ich den Code bereits (Reference Source).
BTW: wenn du mit "IOCP-Thread" den Thread meinst, der den IO Completion Port "pollt", dann ist es richtig was du sagst, das ist nicht notwendigerweise der selbe Thread der auch BeginXxx aufgerufen hat. Heisst aber auch nicht dass es notwendigerweise ein anderer Thread ist. Die Frage ist: ist es garantiert dass der "IOCP-Thread" weiterlebt, bis alle in seinem Context initiierten IOs (wie in dem Beispiel das BeginReceive in ReceiveCallback) abgeschlossen sind?
-
OK.
Habe selbst die Stelle in der sscli gefunden wo ersichtlich ist wie es geht..NET verwaltet ja anscheinend mehrere Thread-Pools, nämlich die normalen "worker threads", dazu "wait threads", und eben auch etliche "completion port threads".
Die "completion port threads" kümmern sich wie der Name vermuten lässt um die Ausführung von completion callbacks.
Die Funktion ReceiveCallback im Beispiel oben läuft in einem dieser Threads. BeginReceive wird also auch im Context eines dieser Threads ausgeführt. Wenn dieser "completion port thread" nun sterben gehen würde bevor der IO abgeschlossen ist, wäre das übel.
Nu kommts. Grundsätzlich passt die CLI die Anzahl der "completion port threads" an die aktuellen Bedürfnisse an, d.h. es werden auch mal Threads beendet. Allerdings wird vor dem Beenden eines "completion port threads" netterweise darauf gewartet, dass dieser keine ausständigen IOs mehr hat.
Kurz: das Kommentar in der MSDN stimmt, und man muss an vielen Stellen aufpassen nicht IOs aus einem Thread zu starten der zu früh beendet werden könnte. Die Completion-Callbacks des .NET Frameworks kann man aber getrost verwenden, denn das System kümmert sich darum dass es geht.
EDIT: ich meine natürlich verwenden um weitere IOs zu starten, also diverse BeginXxx Funktionen aufzurufen /EDIT
-
Interessant, hast du nen Link oder ne Referenz?
-
Klar. Leider konnte ich kein leicht verständliches Statement von MS finden (und auch nix in der Doku - vielleicht isses da, habs aber nicht gefunden). Deswegen hab' ich überhaupt erst angefangen selbst im reference-Source/sscli-Source zu suchen. Aber die entsprechende Stelle im sscli Source kann ich dir schon sagen:
sscli20_20060311\sscli20\clr\src\vm\win32threadpool.cpp
Da drin schau dir die Funktion
ThreadpoolMgr::IsIoPending
sowie deren Verwendung an.Die in
IsIoPending
aufgerufene FunktionPAL_GetIOCPThreadIoPendingFlag
ist auch nix magisches, ist hier zu finden:sscli20_20060311\sscli20\pal\win32\win32pal.c
Und im Prinzip nur ein Wrapper um
NtQueryInformationThread
(aus der NTDLL.DLL).----
U.a. dass solche Dinge nicht bzw. nicht ausreichend dokumentiert sind nervt mich immer wieder an Microsoft
-
Danke!