2010-07-01 244 views

回答

7

是的,异步请求通常可以在不花费线程的情况下处理。操作系统对它们有特殊的支持,如重叠的I/O和完成端口等功能。他们实际上做的是利用内核线程的代价,无论如何,因为驱动程序需要能够处理来自多个用户模式程序的多个请求。 .NET框架很容易利用BeginXxx()方法中的优势。

使用线程池线程也很便宜,但是您需要遵守线程池调度程序的行为。这不像启动更多的TP线程那么多核心。 TP线程不应该用于代码,可以保持阻塞一段时间,非常典型的CS任务,如建立连接。

异步代码中的错误处理非常困难。当EndXxxx()方法引发异常时,通常情况下只有很少的上下文。它发生在一个回调线程上,离主逻辑很远。好吧,当你可以耸耸肩“没有发生,让我们记录下来”,当程序的状态取决于它时,总体情况和红色。在后一种情况下总是选择同步模式。

+1

+1它们甚至比这更好:通常不需要内核线程(大多数驱动程序甚至没有线程)。 – 2010-07-01 22:30:09

+0

+1 vrey有用的答案。深深的谢谢! – 2010-07-01 22:46:47

0

在您对执手锁同步服务器当访问你的数据结构时(如果有更新),这需要时间和代码(并且是难以发现错误的来源)。在许多实现中(例如,用于堆栈分配)也具有许多(例如数千)线程带来了技术问题,并且如果服务器是IO限制,那些线程几乎全部正在休眠(等待网络)并且正在浪费存储器。在一个线程中使用异步模型,您可以忽略锁定问题(这意味着您的处理速度尽可能快),并且仅使用客户端实际需要的内存(只有一个堆栈)。

但是现在多核机器很常见,所以部分优势就会丢失(因为如果修改共享数据结构就必须锁定)。在N个异步服务器前面使用平衡器可能会获得最佳效率,其中N是您的环境的最佳线程数。

异步方法的坏处在于,根据您的工具,代码可能非常难看并且难以理解,如果计算不是微不足道的,并且错误地处理进入无限循环,则整个异步服务器将无响应(所以应该添加一个看门狗)。

+0

异步并不解决锁定问题。您可以通过不同线程上的多个回调重新输入异步回调。使用异步模型时,您仍然需要保护任何共享状态。 – heavyd 2010-07-01 22:14:27

+0

使用抢先式多线程编码正确锁定的问题是切换是“不可见的”,并且可能随时发生。 异步实现中的切换点是固定的,通常是绝对明显的。由于确定性,调试也更简单。 尽管仍然可能存在某种形式的“逻辑”锁定,但在我看来,与在共享数据更新方法中锁定权限的困难相比,没有什么可比的。 – 6502 2010-07-01 22:35:59

+0

@heavyd:重新阅读您的评论在我看来,术语存在问题。我说过使用单线程异步实现,你可以忘记锁定问题......(这意味着关于并发访问的物理锁定,而不是自动机的状态改变之间的逻辑锁定,当然这是强制的),并且如果异步没有加上单线程,那么这个优点就失去了。 在您的评论中,您会讨论不同的主题,因此可能会对术语产生误解。 – 6502 2010-07-02 06:38:00

2

您不想阻止用户界面。通过异步操作,您可以在等待服务器响应时执行其他操作。

2

异步模式让您在同步模式让您等待时继续处理。

+0

简单和简短的答案,但足够的解释。 感谢@Brian – 2015-08-12 06:51:05

1

用汉斯的回答“标记”:I/O操作与线程的独立性允许更显着的缩放;成千上万的未完成的请求是可能的,这是无法使用线程完成的。

另外,当你启动considering the complexities of error handling in protocol design时,事实证明异步方法的复杂程度远远低于正确编写同步代码的复杂度。大多数同步套接字代码看起来更简单,但实际上包含微妙的错误。

如果双方发送的数据比读取的数据多,则异步方法对于防止死锁情况也很重要;有关更多讨论,请参阅this blog post

如果您希望在线程安全封装器(具有更简单的错误处理)中实现异步I/O的可靠性优势和(大部分)性能优势,请考虑Nito.Async库。