2017-10-16 135 views
6

有一個TcpListener類的服務器。它使用BeginAcceptTcpClient(AsyncCallback,Object)方法接受傳入連接。異步方法在當前線程(主線程)中啓動AsyncCallback

的代碼被寫入例如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 
    } 
} 

的問題是,所述DoAcceptTcpClientCallback(IAsyncResult的AR)方法有時開始在當前線程執行(主),而不是新的,並阻斷它(主)。因此,無法接收以下連接。 請幫助理解爲什麼不創建此方法的線程

+0

你沒有向我們展示過,但我想象一下'//從客戶端接收消息是由於某種原因切換回使用* synchronous * API調用。不要這樣做。一旦你走異步(以某種不涉及創建線程的方式),你不知道你將運行什麼線程 - 所以你應該只運行,只要你需要分派下一個異步方法和然後返回。 –

+0

首先,您爲什麼不使用[AcceptTcpClientAsync](https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.accepttcpclientasync%28v=vs.110%29.aspx?f = 255&MSPPError = -2147217396)?它在所有支持的.NET版本中都可用,並且使異步編程更容易。 –

+0

第二個「異步」並不意味着「不同的線程」。這意味着你的線程不會阻止等待響應。 IO操作甚至可能不需要單獨的線程,因爲Windows中的IO在驅動程序級別上始終是異步的。 API *模擬*阻塞,使單線程編程更容易 –

回答

2

是的,正如您發現的那樣,you're not guaranteed that your AsyncCallback is called on a new thread。基本上,如果一個異步操作完成得如此之快,以至於回調可以從同一個線程同步運行,那麼它將會。

BeginXXX調用想要在舊的異步模型中返回時,會發生這種情況,您等待發生的事情已經發生,例如,在你的情況下一個連接,所以爲了防止不必要的上下文切換,它將IAsyncResult.CompletedSynchronously設置爲true並同步執行你的回調,在你的例子中無限地阻塞while (true)循環中的線程,決不讓它從BeginAcceptTcpClient調用中返回。

您應該在您的回調方法中考慮該情況,方法是繼續保持異步並快速返回。

另外,請使用async/await,它們使異步編程更容易。