我有一个。NET Windows服务,它使用BeginRead/EndRead异步I/O范例实现套接字服务器。现在这个套接字代码需要调用一些async/Task/await异步代码。
我一直在使用nito.asyncex库的AsyncContext Run方法,但我对从EndRead调用它是否会阻塞,从而使工作线程成为人质持保留态度。对于前面的问题,我得到的建议是使用Task.run而不是Nito.Asyncex的AsyncContext.run。这会将调用提交到async/await代码中,并立即返回。我突然想到,在负载情况下,客户端没有反推来防止请求淹没线程池。
我将重新提出我最初关于Nito.AsyncEx's AsyncContext.Run的问题:它是将它调用的线程(池线程调用我的套接字的EndRead回调)作为人质,还是在它调用的异步I/O在后台发生时释放该线程?
如果Nito.Asyncex的AsyncContext.Run确实阻塞,那么Task.Run似乎是我唯一的选择。有没有关于如何推迟客户端请求以防止线程池耗尽的建议?
我建议你重新考虑到目前为止的每一个假设:
如果您确定确实需要实现自己的TCP/IP服务器,并且在回调中无法同步完成工作,那么您确实需要节流。。。然后考虑反应性扩展或TPL数据流。这两个库都有可选的内置节流功能。
这取决于您的应用程序到底在做什么。下面是一些场景:
您正在处理来自每个客户端的数据流
每个客户机发送一些数据,服务器在每条消息传入时处理它。在这种情况下,可能不需要在单独的线程中运行处理。当您阻塞时,TCP堆栈正在建立一个缓冲区供您读取。如果剩余缓冲区大小太小,TCP堆栈将向客户端发送窗口大小警告。
客户端可能会发送一系列消息,每个消息都需要长时间的处理
客户端以突发方式向服务器发送消息。每个消息都需要相当长的时间来处理。以串行方式处理消息会导致大量的TCP重试,从而产生问题。在本例中,启动
null
处理来自客户端的消息是很耗时的,而且客户端实际上发送的工作可能比您所能处理的更多。在这种情况下,您可能需要某种特定于应用程序的流控制。客户端可能需要在发送消息之前轮询服务器状态。如果客户无论如何发送,您将被迫忽略这些信息。