使用Task.Run以致线程池耗尽时会发生什么?

时间:2014-01-24 18:55:32

标签: .net task-parallel-library threadpool async-await

我有一个.NET Windows服务,它使用BeginRead / EndRead异步I / O范例实现套接字服务器。现在这个套接字代码需要调用一些async / Task / await异步代码。

我一直在使用Nito.AsyncEx库的AsyncContext类'Run方法,但我对从EndRead调用它是否会阻塞,保持工作线程人质有所保留。我得到my earlier question的建议是使用Task.Run而不是Nito.AsyncEx的AsyncContext.Run。这会将调用提交到async / await代码并立即返回。在我看来,在负载下,客户端没有阻止请求来充斥线程池。

我将重新询问我关于Nito的原始问题.AsyncEx的AsyncContext.Run:它是否保持调用的线程(池线程调用我的套接字的EndRead回调)作为人质,或者它是否在异步时释放该线程它调用的I / O是在后台发生的吗?

如果Nito.AsyncEx的AsyncContext.Run真正阻止,那么Task.Run似乎是我唯一的选择。关于如何回击客户端请求以防止线程池耗尽的任何建议?

2 个答案:

答案 0 :(得分:7)

AsyncContext.Run的目的是阻止所有异步操作完成。它确实保留了线程,直到发生这种情况。

我建议你重新考虑到目前为止的每一个假设:

  1. 你真的需要套接字服务器吗? TCP / IP套接字周围存在的陷阱。认真。 很多他们。是否有任何方式可以自行托管WebAPI?与套接字服务器相比, lot 更容易使用。
  2. 为什么需要将工作推送到线程池线程?已在线程池线程上调用End*回调。
  3. 你确定需要节流吗?没有代码可以阻止充满动力的DoS攻击。
  4. 如果您确定需要实现自己的TCP / IP服务器,并且无法在回调中同步工作,并且确实需要限制...那么请考虑Reactive ExtensionsTPL Dataflow。这两个库都内置了可选的限制。

答案 1 :(得分:2)

这取决于您的应用程序究竟在做什么。以下是一些场景:

您正在处理来自每个客户的数据流

每个客户端都发送一些数据,您的服务器在处理每个消息时对其进行处理。在这种情况下,可能不需要在单独的线程中运行处理。在阻塞时,TCP堆栈正在构建一个缓冲区供您阅读。如果剩余的缓冲区大小太小,TCP堆栈将向客户端发送窗口大小警告。

客户可能会发送一连串消息,每个消息都需要经过漫长的处理

客户端以突发方式向您的服务器发送消息。每条消息都需要相当长的时间来处理。串行处理消息会导致大量TCP重试,并且会出现问题。在这种情况下,请关闭Task.Run并且不要担心耗尽线程池。

客户端可能会发送的数据远远超出您处理的数据

处理来自客户端的消息非常耗时,客户可能会实际发送比您处理的更多的工作。在这种情况下,您可能需要某种特定于应用程序的流量控制。也许客户端需要在发送消息之前轮询服务器状态。如果客户端仍然发送,您将被迫忽略这些消息。