I'm making an online communication application, and I'd like to process messages asynchronously. I found async-await pattern useful in implementing message loop.
Below is what I have got so far:
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;
}
}
}
Then I got some warning saying that in NewMemberCallback, the call to MessageLoop is not awaited. I actually don't need to await the MessageLoop method because the method does not return until the connection is disconnected. Is it considered a good practice to use async like this? I heard that not awaiting an async method is not good, but I also heard that I should eliminate unnecessary await's. Or is it even considered bad to use async pattern for message loop?
Usually you want to keep track of the task(s) you've started, to avoid re-entrancy and handle exceptions. Example:
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;
}
}
Check Lucian Wischik's "Async re-entrancy, and the patterns to deal with it".
If you can have multiple MessageLoop
instances, then you wouldn't have to worry about re-entrancy, but you'd still want to observe exceptions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With