试图弄清楚我是否应该使用异步方法,例如:
和
与其同步TcpListener.AcceptTcpClient
和NetworkStream.Read
版本相对。我一直在看相关的线程,但我仍然有点不确定:
问题:使用异步方法的主要优点是GUI没有被锁定。但是,这些方法将在单独的Task线程上调用,因此不存在威胁。此外,TcpListener.AcceptTcpClient
阻塞线程,直到建立连接,因此没有浪费的CPU周期。既然如此,为什么这么多人总是建议使用异步版本?在这种情况下,似乎同步版本会更优秀吗?
此外,使用异步方法的另一个缺点是增加了对象的复杂性和持续投射。例如,必须这样做:
private void SomeMethod()
{
// ...
listener.BeginAcceptTcpClient(OnAcceptConnection, listener);
}
private void OnAcceptConnection(IAsyncResult asyn)
{
TcpListener listener = (TcpListener)asyn.AsyncState;
TcpClient client = listener.EndAcceptTcpClient(asyn);
}
与此相反:
TcpClient client = listener.AcceptTcpClient();
同样,由于必须创建另一个线程,异步版本会有更多的开销。 (基本上,每个连接都有一个线程,然后在读取该线程时也会有另一个线程.Threadception!)
此外,还有TcpListener的装箱和拆箱以及与创建,管理和关闭这些额外线程相关的开销。
基本上,通常会有单独的线程来处理单个客户端连接,现在有了这个,然后为每种类型的操作执行了一个额外的线程(读取/写入流数据并在服务器端侦听新连接)
如果我错了,请纠正我。我仍然是线程新手,我试图理解这一切。但是,在这种情况下,似乎使用普通的同步方法,只是阻塞线程将是最佳解决方案?
答案 0 :(得分:4)
TcpListener.AcceptTcpClient阻塞线程,直到建立连接,因此没有浪费的CPU周期。
但也没有工作要做。线程是一个非常昂贵的操作系统对象,大概是最昂贵的。当线程阻塞连接请求时,你的程序正在消耗一兆字节的内存,而不会被使用。
但是,这些方法将在单独的Task线程上调用,因为它不存在
的威胁
任务也不是一个好的解决方案,它使用线程池线程,但线程将阻塞。线程池管理器尝试将运行的TP线程数保持等于计算机上的cpu核心数。当TP线程长时间阻塞时,这将无法正常工作。它阻止其他有用的工作由等待轮到他们的其他TP线程完成。
BeginAcceptTcpClient()使用所谓的I / O完成回调。套接字正在侦听时不会消耗系统资源。一旦连接请求进入,操作系统就会运行一个APC(异步过程调用),它会抓取一个线程池线程来进行回调。线程本身通常使用几微秒。很有效率。
在下一个版本的C#中使用下一个 async 和等待关键字,这种代码将变得更加简单。年底,也许。
答案 1 :(得分:3)
如果你在任何线程上调用AcceptTcpClient()
,那么在你获得连接之前该线程是无用的。
如果你调用BeginAcceptTcpClient()
,调用线程可以立即停止,而不会浪费线程。
这在使用ThreadPool(或TPL)时尤其重要,因为它们使用有限数量的池线程。
如果你有太多的线程在等待操作,你可以用完线程池线程,这样新的工作项就必须等到其他一个线程完成。