2017-10-16 90 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,它们使异步编程更容易。