【问题标题】:Understanding async/await to manage multiple clients了解 async/await 以管理多个客户端
【发布时间】:2016-04-23 12:11:09
【问题描述】:

我对@9​​87654323@ 感到困惑,因为我可能仍然不明白它的正确用法。

我有一个简单的WPF-UI 和一个ViewModel-方法来开始监听想要连接的客户端。 当用户点击按钮开始收听时,会执行以下方法:

public void StartListening()
{
    _tcpListener.Start(); // TcpListener
    IsListening = true; // bool
    Task.Factory.StartNew(DoStartListeningAsync, TaskCreationOptions.LongRunning);
}

调用的方法DoStartListeningAsync定义如下

private async Task DoStartListeningAsync()
{
    while (IsListening)
    {
        using (var newClient = await _tcpListener.AcceptTcpClientAsync() /*.WithWaitCancellation(_cts.Token)*/)
        {
            apiClient = new ApiClient();
            if(await apiClient.InitClientAsync()) // <-- here is the problem
            {
                // ... apiClient is now initialized
            }

            // ... do more and go back to await _tcpListener.AcceptTcpClientAsync()
        }
    }
}

ApiClient 类的InitClientAsync 方法定义如下:

public async Task<bool> InitClientAsync()
{
    using (var requestStream = await _apiWebRequest.GetRequestStreamAsync())
    {
        _apiStreamWriter = new StreamWriter(requestStream);
    }

    // ... do somehing with the _apiStreamWriter

    return true;
}

但是,有时InitClientAsync-调用会卡在await _apiWebRequest.GetRequestStreamAsync(),然后将冻结DoStartListeningAsync-方法在// &lt;-- here is the problem 的执行。 如果DoStartListeningAsync 被卡住,将不会处理任何新连接,这会破坏我异步处理多个客户端的整个概念。

【问题讨论】:

  • 您可能想阅读 Eric Lippert 的 Asynchronous Programming in C# 5.0 part two: Whence await?:“异步方法的全部意义在于您尽可能地留在当前线程上”
  • 可能是一个单独的问题,但您是否应该在将 requestStream 传递给 StreamWriter 之后,在使用 _apiStreamWriter 之前对其进行处理?
  • 作为一般规则,您应该使用 Task.Run 而不是 Task.Factory.StartNew 与 async-await。
  • @i3arnon:你能详细解释一下为什么吗?

标签: c# multithreading asynchronous async-await


【解决方案1】:

由于您在代码路径中使用“await”关键字,因此您实际上不会提供服务

多个客户端异步。

问题是,您在后台线程中的代码将一一服务于客户端。更深入地了解 - 在 while 循环中,您正在获取请求流,等到它被加载,服务它,然后等待其他请求流。

async/await 原则本身并不提供同时服务于多个操作的能力。它正在做的唯一一件事 - 防止阻塞当前线程被其他代码重用。因此,如果使用async/await,您允许系统使用您当前的任务线程,同时等待其他异步操作完成(如_apiWebRequest.GetRequestStreamAsync())。

但是由于您有一个任务,并且您正在等待 while 循环的每次迭代 - 如果您完全同步编写代码,您的代码将以相同的方式工作。唯一的好处是您正在使用 Task,因此 .Net 可以在您等待异步操作完成时重用线程池中的线程。

如果您不想异步地为多个客户端提供服务,您应该启动多个任务,或者不要等到请求完全服务完毕 - 所以实际上从您的代码中删除一些等待。

所以你应该转向设计,你有一个监听任务/线程,除了读取请求并将其放入某个队列之外,它什么都不做。还有其他任务,服务于请求,从队列中读取它。

如果我对您的理解正确,您就是在后台使用 TcpListener。因此,您需要的是接受新客户端的循环,并开始在不同的线程/任务中为它们提供服务而无需任何等待,因此直接接受其他客户端。但是您可以并且应该在为客户端提供服务的处理程序中使用async/await。

看看这个answer - 不完全是你的情况(因为我不知道实施的所有细节),只是为了了解一下。

【讨论】:

    猜你喜欢
    • 2020-03-25
    • 1970-01-01
    • 1970-01-01
    • 2012-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-13
    • 2018-04-12
    相关资源
    最近更新 更多