在c#中使用async方法实现消息循环
本文关键字:方法 实现 消息 循环 async | 更新日期: 2023-09-27 18:05:25
我正在制作一个在线通信应用程序,我想异步处理消息。我发现async-await模式在实现消息循环时很有用。
下面是我到目前为止得到的:
CancellationTokenSource cts=new CancellationTokenSource(); //This is used to disconnect the client.
public Action<Member> OnNewMember; //Callback field
async void NewMemberCallback(ConnectionController c, Member m, Stream stream){
//This is called when a connection with a new member is established.
//The class ConnectionController is used to absorb the difference in protocol between TCP and UDP.
MessageLoop(c, m,stream,cts.Token);
if(OnNewMember!=null)OnNewMember(m);
}
async Task MessageLoop(ConnectionController c, Member m, Stream stream, CancellationToken ct){
MemoryStream msgbuffer=new MemoryStream();
MemoryStream buffer2=new MemoryStream();
while(true){
try{
await ReceiveandSplitMessage(stream, msgbuffer,buffer2,ct); //This stops until data is received.
DecodeandProcessMessage(msgbuffer);
catch( ...Exception ex){
//When the client disconnects
c.ClientDisconnected(m);
return;
}
}
}
然后我得到一些警告说,在NewMemberCallback中,对messagelloop的调用没有等待。我实际上不需要等待MessageLoop方法,因为该方法在连接断开之前不会返回。像这样使用异步被认为是一个好的实践吗?我听说不等待异步方法是不好的,但我也听说我应该消除不必要的等待。或者在消息循环中使用异步模式被认为是不好的?
通常您希望跟踪已启动的任务,以避免重入并处理异常。例子:
Task _messageLoopTask = null;
async void NewMemberCallback(ConnectionController c, Member m, Stream stream)
{
if (_messageLoopTask != null)
{
// handle re-entrancy
MessageBox.Show("Already started!");
return;
}
_messageLoopTask = MessageLoop(c, m,stream,cts.Token);
if (OnNewMember!=null) OnNewMember(m);
try
{
await _messageLoopTask;
}
catch (OperationCanceledException ex)
{
// swallow cancelation
}
catch (AggregateException ex)
{
// swallow cancelation
ex.Handle(ex => ex is OperationCanceledException);
}
finally
{
_messageLoopTask = null;
}
}
查看Lucian Wischik的"异步重入,以及处理它的模式"。
如果您可以有多个MessageLoop
实例,那么您不必担心可重入性,但您仍然希望观察异常。
如果没有await MessageLoop(c, m,stream,cts.Token);
,方法将在遇到第一个await
时返回,然后执行方法的其余部分。这将是一场欲罢不能的场景。异常不会在UI线程上引发,所以如果c.ClientDisconnected(m);
抛出,它将在后台线程上引发并导致显式吞下的异常,因为从该方法返回的Task
中存储的任何异常都不会被观察到。你可以在@Noseratio的这篇文章中找到更多信息
老实说,这似乎有点不合常规。
是否有更好的方法来确定客户端是否已断开连接?您将错过您可能希望向用户或日志显示的任何重要异常。