首页 文章

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

提问于
浏览
2

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

我一直在使用Nito.AsyncEx库's AsyncContext class' Run方法,但我对是否从EndRead调用它会阻止,将工作线程保持为人质有所保留 . 我得到my earlier question的建议是使用Task.Run而不是Nito.AsyncEx 's AsyncContext.Run. This submits the call into the async / await code and returns immediately. It occurred to me that under load that there' s没有对客户端进行回击以防止请求泛滥线程池 .

我会重新问我关于Nito.AsyncEx的AsyncContext.Run原题:是否持有线程,它被称为上(池线程调用我的套接字的EndRead回调)为人质,或者它腾出线程,而异步I / O它的调用是在后台发生的?

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

2 回答

  • 7

    AsyncContext.Run 的目的是阻塞,直到所有异步操作都完成 . 它确实持有线程,直到发生这种情况 .

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

    • 你真的需要套接字服务器吗?围绕TCP / IP套接字存在 tons 陷阱 . 认真 . 其中 lot . 有没有办法可以自行托管WebAPI?工作比套接字服务器容易得多 .

    • 为什么需要将工作推送到线程池线程?已经在线程池线程上调用 End* 回调 .

    • 你确定需要节流吗?没有代码可以阻止充分动机的DoS攻击 .

    如果您在回调中同步进行工作,并且确实需要限制...那么请考虑Reactive ExtensionsTPL Dataflow . 这两个库都内置了可选的限制 .

  • 2

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

    You are processing a stream of data from each client

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

    Clients may send a burst of messages each requiring lengthy processing

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

    Clients may send far more data than you can process

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

相关问题