2017-10-16 91 views
6

Istnieje serwer klasy TcpListener. Przyjmuje połączenia przychodzące za pomocą metody BeginAcceptTcpClient (AsyncCallback, Object).Metoda asynchroniczna uruchamia AsyncCallback w bieżącym wątku (wątek główny)

Kod jest napisany w przykładzie MSDN

public static ManualResetEvent tcpClientConnected = 
    new ManualResetEvent(false); 

public static void DoBeginAcceptTcpClient(TcpListener 
    listener) 
{ 
    while(true) 
    { 
     tcpClientConnected.Reset(); 
     Console.WriteLine("Waiting for a connection..."); 
     listener.BeginAcceptTcpClient(
      new AsyncCallback(DoAcceptTcpClientCallback), 
      listener); 
     tcpClientConnected.WaitOne(); 
    } 
} 
public static void DoAcceptTcpClientCallback(IAsyncResult ar) 
{ 
    TcpListener listener = (TcpListener) ar.AsyncState; 
    TcpClient client = listener.EndAcceptTcpClient(ar); 
    Console.WriteLine("Client connected completed"); 
    tcpClientConnected.Set(); 
    while(true) 
    { 
     //Receiving messages from the client 
    } 
} 

Problemem jest to, że DoAcceptTcpClientCallback (IAsyncResult ar) metoda czasami rozpoczyna wykonywanie w bieżącym wątku (główny), nie jest nowy, a bloki to (główne). Z tego powodu nie można uzyskać następujących połączeń. Proszę zrozumieć, dlaczego nie utworzyć wątku dla tej metody.

+0

Nie pokazałeś nam, ale wyobrażam sobie, że '// Odbieranie wiadomości od klienta' z jakiegoś powodu wraca do korzystania z * synchronicznych * wywołań API. Nie rób tego. Gdy przejdziesz do asynchronizacji (w pewien sposób, który nie wymaga tworzenia wątków), nie wiesz, jakiego wątku będziesz używać - więc powinieneś uruchomić tylko tak długo, jak to konieczne, aby wysłać następną metodę asynchroniczną i następnie wróć. –

+0

Po pierwsze, dlaczego nie używasz [AcceptTcpClientAsync] (https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.accepttcpclientasync%28v=vs.110%29.aspx?f = 255 & MSPPError = -2147217396)? Jest dostępny we wszystkich obsługiwanych wersjach .NET i znacznie ułatwia programowanie asynchroniczne. –

+0

Drugi "asynchroniczny" nie oznacza "innego wątku". Oznacza to, że twój wątek nie będzie blokował czekania na odpowiedź. Operacje IO mogą nawet nie wymagać oddzielnego wątku, ponieważ IO w systemie Windows jest zawsze asynchroniczne na poziomie sterownika. API * emuluje * blokowanie, aby ułatwić programowanie jednowątkowe –

Odpowiedz

2

Tak, jak się dowiedziałeś, you're not guaranteed that your AsyncCallback is called on a new thread. Zasadniczo, jeśli operacja asynchroniczna zakończy się tak szybko, że wywołanie zwrotne może zostać uruchomione synchronicznie z tego samego wątku, będzie.

To się dzieje, gdy do czasu połączenia BeginXXX chcesz powrócić w starym asynchronicznym modelu, zadanie, na które czekałeś, już się stało, np. w twoim przypadku połączenie, aby zapobiec niepotrzebnemu przełączeniu kontekstu, ustawia wartość IAsyncResult.CompletedSynchronously na true i wykonuje synchronizację zwrotną, która w twoim przykładzie blokuje w nieskończoność wątek w pętli while (true), nigdy nie pozwalając jej powrócić z połączenia BeginAcceptTcpClient.

Powinieneś uwzględnić ten scenariusz w swojej metodzie zwrotnej, kontynuując pozostawanie asynchronicznym i szybkie zwracanie.

Zobacz również, korzystając z async/await, znacznie ułatwiają programowanie asynchroniczne.