我已经确定了TCP应用程序中的一个瓶颈,出于这个问题,该瓶颈已得到简化。
我有一个MyClient
类,它表示客户端何时连接。我也有一个MyWrapper
类,它表示满足某些条件的客户端。如果MyClient
满足某些条件,则符合包装条件。
我想公开一个允许调用方等待MyWrapper
的方法,该方法应处理无效MyClients
的协商和拒绝:
public static async Task StartAccepting(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var wrapper = await AcceptWrapperAsync(token);
HandleWrapperAsync(wrapper);
}
}
因此,
AcceptWrapperAsync
等待有效的包装器,并且HandleWrapperAsync
异步处理包装器,而不会阻塞线程,因此AcceptWrapperAsync
可以尽快恢复工作。该方法在内部如何工作是这样的:
public static async Task<MyWrapper> AcceptWrapperAsync(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
var client = await AcceptClientAsync();
if (IsClientWrappable(client))
return new MyWrapper(client);
}
return null;
}
public static async Task<MyClient> AcceptClientAsync()
{
await Task.Delay(1000);
return new MyClient();
}
private static Boolean IsClientWrappable(MyClient client)
{
Thread.Sleep(500);
return true;
}
此代码模拟每秒有一个客户端连接,并且检查连接是否适合包装器需要半秒钟。
AcceptWrapperAsync
循环直到生成有效的包装器,然后返回。这种方法效果很好,但存在缺陷。在
IsClientWrappable
执行期间,无法接受其他客户端,这在许多客户端尝试同时连接时会造成瓶颈。恐怕在现实生活中,如果服务器在连接许多客户端的情况下关闭,那么运行起来就不好了,因为所有这些客户端都将尝试同时连接。我知道同时连接所有这些对象非常困难,但是我想加快连接过程。使
IsClientWrappable
异步,只会确保在协商结束之前不会阻塞执行线程,但无论如何都会阻塞执行流程。我如何才能改善这种方法以不断接受新客户,但仍然能够使用
AcceptWrapperAsync
等待包装器? 最佳答案
//this loop must never be blocked
while (!token.IsCancellationRequested)
{
var client = await AcceptClientAsync();
HandleClientAsync(client); //must not block
}
Task HandleClientAsync(Client client) {
if (await IsClientWrappableAsync(client)) //make async as well, don't block
await HandleWrapperAsync(new MyWrapper(client));
}
这样,您就可以将
IsClientWrappable
逻辑移出accept循环,并移入后台异步工作流中。如果您不希望
IsClientWrappable
成为非阻塞对象,则只需将其与Task.Run
包装在一起即可。 HandleClientAsync
不能阻塞是很重要的,这样它的调用者也不会阻塞。