【发布时间】:2010-07-01 21:44:14
【问题描述】:
我有一个客户端-服务器项目,正在寻找更好的方式来处理来自客户端的请求。有人建议异步模式优于同步模式和线程池模式。
我的问题是为什么?异步模式有什么缺点吗?
【问题讨论】:
标签: c# asynchronous client-server
我有一个客户端-服务器项目,正在寻找更好的方式来处理来自客户端的请求。有人建议异步模式优于同步模式和线程池模式。
我的问题是为什么?异步模式有什么缺点吗?
【问题讨论】:
标签: c# asynchronous client-server
是的,通常可以在不消耗线程的情况下处理异步请求。操作系统对它们有特殊的支持,如重叠 I/O 和完成端口等功能。他们本质上所做的是利用内核线程的开销,无论如何都需要内核线程,因为驱动程序需要能够处理来自多个用户模式程序的多个请求。 .NET 框架很容易在其 BeginXxx() 方法中利用这一点。
使用线程池线程也很便宜,但您会受到线程池调度程序的影响。这不太喜欢启动更多的 TP 线程然后有内核。 TP 线程永远不应该用于可能会被阻塞一段时间的代码,这对于建立连接之类的 CS 任务来说非常典型。
异步代码中的错误处理非常困难。当 EndXxxx() 方法引发异常时,您通常只有很少的上下文。它发生在一个回调线程上,离主逻辑很远。好吧,当您可以耸耸肩“没有发生,让我们记录它”时,当程序的状态取决于它时,完全混乱和重振。在后一种情况下始终选择同步模式。
【讨论】:
您不想阻止用户界面。通过异步操作,您可以在等待服务器响应的同时做其他事情。
【讨论】:
异步模式让您继续处理,而同步模式让您等待。
【讨论】:
同步操作的一个缺点是 IMO 不能中断它们 - 例如当您的服务器应用程序调用同步方法 WaitForConnection() 并且没有客户端连接时,您无法停止等待...
例如试试看这个What is a good way to shutdown Threads blocked on NamedPipeServer#WaitForConnection?
【讨论】:
按照 Hans 的回答“标记”:I/O 操作与线程的独立性允许更显着的扩展;数以万计的未完成请求是可能的,这根本无法使用线程来完成。
另外,当你启动considering the complexities of error handling in protocol design 时,发现异步方法的复杂度远低于正确编写同步代码的复杂度。大多数同步套接字代码看起来更简单,但实际上包含一些细微的错误。
如果双方发送的数据多于读取的数据,异步方法对于防止死锁情况也很重要;更多讨论请参见this blog post。
如果您希望线程安全包装器中异步 I/O 的可靠性优势和(大部分)性能优势(具有更简单的错误处理),请考虑使用 Nito.Async 库。
【讨论】:
在同步服务器中,您必须在访问数据结构时处理锁(如果存在更新),这需要时间和代码(并且是难以找到错误的来源)。同样拥有许多(例如数千个)线程会在许多实现(例如堆栈分配)中带来技术问题,如果服务器受 IO 限制,那么这些线程几乎都处于休眠状态(等待网络)并且只是浪费内存。
在单线程上使用异步模型,您可以忽略锁定问题(这意味着您的处理速度尽可能快)并且您只使用客户端实际需要的内存(只有一个堆栈)。
然而,现在多核机器相当普遍,因此失去了部分优势(因为如果修改共享数据结构,您必须锁定)。在 N 个异步服务器前使用平衡器可能会获得最佳效率,其中 N 是您的环境的最佳线程数。
异步方法的不好的一面是,根据您的工具,代码可能非常丑陋且难以理解,并且如果计算不是微不足道的并且您的处理错误地进入了无限循环,则整个异步服务器将无响应(所以可能应该添加一个看门狗)。
【讨论】: