Understanding async/await to manage multiple clien

2019-07-22 13:49发布

I'm getting confused by await/async as I may still not get the point of its correct usage.

I have a simple WPF-UI and a ViewModel-method to start listening for clients which want to connect. The following method is executed when the user clicks the button to start listening:

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

The method DoStartListeningAsync which is called is defined like

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()
        }
    }
}

The ApiClient class' InitClientAsync method is defined like:

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

    // ... do somehing with the _apiStreamWriter

    return true;
}

However, sometimes the InitClientAsync-call will get stuck at await _apiWebRequest.GetRequestStreamAsync() which then will freeze the execution of the DoStartListeningAsync-method at // <-- here is the problem. In case the DoStartListeningAsync is stuck, no new connections will be handled which destroys my whole concept of handling multiple clients asynchronously.

1条回答
兄弟一词,经得起流年.
2楼-- · 2019-07-22 14:33

Since you are using "await" keyword along the code path, you won't actually serve

multiple clients asynchronously.

The thing is, your code in the background thread will serve clients one by one. Take a deeper look - in the while loop you are getting request stream, wait till it is loaded, serve it, and then wait for other request stream.

async/await principle doesn't itself provide ability to serve multiple actions at the time. The only thing it is doing - prevents blocking current thread from being reusable by other code. So if going with async/await, you allow system yo use your current task thread, while it is waiting to other async action to complete (like _apiWebRequest.GetRequestStreamAsync()).

But since you are having one task, and you are waiting on every iteration of while loop - your code will work the same way, if you wrote it completely synchronous. The only profit is that you are using Task, and so .Net can reuse it's thread from thread pool while you are waiting for async actions to complete.

If you wan't to serve multiple clients asynchronously, you should either start multiple tasks, or don't wait till request is completely served - so actually remove some awaits from your code.

So you should move towards design, there you have one listening task/thread, that does nothing exept reading requests and putting it to the some queue. And having other tasks, that serve requests, reading it from the queue.

If I understood you correctly, you are using TcpListener under the hood. So what you need, is the loop where you accept new clients, and start serving them in the different thread/task without any waiting, so going directly to accepting other clients. But you can and should use async/await inside those handlers that serve clients.

Take a look at this answer - not completely your case (since I don't know all details of implementation), but just to get the idea.

查看更多
登录 后发表回答