我已经确定了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不能阻塞是很重要的,这样它的调用者也不会阻塞。

10-06 06:30