我对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
被卡住,将不会处理新的连接,这会破坏我异步处理多个客户端的整个概念。
答案 0 :(得分:3)
由于您在代码路径中使用“await”关键字,因此您实际上不会提供
多个客户端异步。
问题是,后台线程中的代码将逐个为客户端提供服务。深入了解 - 在while循环中,您获取请求流,等待它加载,提供服务,然后等待其他请求流。
async/await
原则本身并不能提供当时为多个行动提供服务的能力。它唯一做的事情 - 阻止阻止当前线程被其他代码重用。因此,如果使用async/await
,则允许系统使用当前任务线程,同时等待其他异步操作完成(如_apiWebRequest.GetRequestStreamAsync()
)。
但是因为你有一个任务,并且你正在等待while循环的每一次迭代 - 你的代码将以相同的方式工作,如果你完全同步编写它。唯一的好处是您正在使用Task,因此.Net可以在等待异步操作完成时从线程池中重用它的线程。
如果您不想异步地为多个客户端提供服务,您应该启动多个任务,或者不要等到请求完全提供 - 所以实际上从代码中删除了一些等待。
所以你应该转向设计,你有一个监听任务/线程,它不会执行任何操作读取请求并将其放入某个队列。还有其他任务,用于处理请求,从队列中读取它。
如果我理解正确,你就是在引擎盖下使用TcpListener。所以你需要的是你接受新客户端的循环,并开始在不同的线程/任务中提供它们而不需要等待,因此直接接受其他客户端。但您可以而且应该在为客户提供服务的处理程序中使用async/await
。
看看这个answer - 不完全是你的情况(因为我不知道实施的所有细节),只是为了得到这个想法。