了解async / await来管理多个客户端

时间:2016-01-18 10:44:10

标签: c# multithreading asynchronous async-await

我对await/async感到困惑,因为我仍然无法正确使用它。

我有一个简单的WPF - 用户界面和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方法在// <-- here is the problem处的执行。 如果DoStartListeningAsync被卡住,将不会处理新的连接,这会破坏我异步处理多个客户端的整个概念。

1 个答案:

答案 0 :(得分:3)

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

  

多个客户端异步。

问题是,后台线程中的代码将逐个为客户端提供服务。深入了解 - 在while循环中,您获取请求流,等待它加载,提供服务,然后等待其他请求流。

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

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

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

所以你应该转向设计,你有一个监听任务/线程,它不会执行任何操作读取请求并将其放入某个队列。还有其他任务,用于处理请求,从队列中读取它。

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

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