我正在制作一个在线通信应用程序,我想异步处理消息。我发现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中,不等待对MessageLoop的调用。 我实际上不需要等待MessageLoop方法,因为该方法在连接断开之前不会返回。 像这样使用异步被认为是一种好习惯吗?我听说不等待异步方法并不好,但我也听说我应该消除不必要的等待。或者甚至认为在消息循环中使用异步模式是不好的?
答案 0 :(得分:4)
通常,您希望跟踪已启动的任务,以避免重新入侵和处理异常。例如:
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的"Async re-entrancy, and the patterns to deal with it"。
如果您可以拥有多个MessageLoop
个实例,那么您不必担心重新入侵,但您仍然希望观察异常。
答案 1 :(得分:3)
如果您不await MessageLoop(c, m,stream,cts.Token);
,则只要遇到第一个await
,该方法就会返回,然后执行方法的其余部分。这将是一场火灾和遗忘的场景。 UI线程不会引发异常,因此如果c.ClientDisconnected(m);
抛出,它将在后台线程上引发并导致显式吞下的异常,因为存储在Task
中的任何异常都是从没有观察到方法。您可以通过@Noseratio
老实说,似乎有点不同寻常。
是否有更好的方法来确定客户端是否已断开连接?您将错过您可能希望向用户或日志显示的任何重要例外。