2017-10-16 19 views
6

Il existe un serveur de classe TcpListener. Il accepte les connexions entrantes à l'aide de la méthode BeginAcceptTcpClient (AsyncCallback, Object).La méthode asynchrone démarre AsyncCallback dans le thread en cours (thread principal)

Le code est écrit dans l'exemple 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 
    } 
} 

Le problème est que le DoAcceptTcpClientCallback (IAsyncResult ar) Méthode parfois commence à exécuter dans le thread courant (principal), et non pas la nouvelle, et des blocs c'est (principal). Pour cette raison, les connexions suivantes ne peuvent pas être reçues. S'il vous plaît aider à comprendre pourquoi ne pas créer un fil pour cette méthode

+0

Vous ne nous avez pas montré, mais j'imagine que '// Recevoir des messages du client' est pour une raison quelconque de revenir à l'utilisation d'appels API * synchrones *. Ne fais pas ça. Une fois que vous êtes asynchrone (d'une manière qui n'implique pas la création de threads) vous ne savez pas quel thread vous allez exécuter - donc vous ne devriez courir que le temps nécessaire pour envoyer la prochaine méthode asynchrone et puis revenez. –

+0

Tout d'abord, pourquoi n'utilisez-vous pas [AcceptTcpClientAsync] (https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.accepttcpclientasync%28v=vs.110%29.aspx?f = 255 & MSPPError = -2147217396)? Il est disponible dans toutes les versions .NET prises en charge et rend la programmation asynchrone beaucoup plus facile. –

+0

Deuxième 'asynchrone' ne signifie pas' thread différent'. Cela signifie que votre thread ne bloquera pas l'attente d'une réponse. Les opérations d'E/S peuvent même ne pas avoir besoin d'un thread séparé car les E/S dans Windows sont toujours asynchrones au niveau du pilote. L'API * émule * le blocage pour faciliter la programmation à un seul thread –

Répondre

2

Oui, comme vous avez découvert, you're not guaranteed that your AsyncCallback is called on a new thread. Fondamentalement, si une opération asynchrone se termine si rapidement que le rappel peut être exécuté de manière synchrone à partir du même thread, il le fera. Cela se produit lorsque, au moment où les appels BeginXXX veulent retourner dans l'ancien modèle asynchrone, la chose que vous attendez est déjà arrivée, par ex. Dans votre cas une connexion, afin d'éviter un changement de contexte inutile, il définit IAsyncResult.CompletedSynchronously à true et exécute votre callback de façon synchrone ce qui dans votre exemple bloque infiniment le thread dans la boucle while (true), ne le laissant jamais revenir de l'appel BeginAcceptTcpClient.

Vous devez tenir compte de ce scénario dans votre méthode de rappel en continuant à rester asynchrone et à revenir rapidement.

De même, regardez dans async/await, ils facilitent la programmation asynchrone.