I have read an answer to a similar question https://stackoverflow.com/a/43841624/11478903 but it doesn't explain the yielding of execution.
I have a thread that consumes events using GetConsumingEnumerable
from a BlockingCollection<Event> _eventQueue
property.
public async Task HadleEventsBlocking()
{
foreach (var event in _eventsQueue.GetConsumingEnumerable())
{
switch (event)
{
case Event.BtnAddClicked:
HandleBtnAddClickedAsync(_cts.Token);
break;
case Event.BtnRemoveClicked:
HandleBtnRemoveClickedAsync(_cts.Token);
break;
case Event.BtnDisableClicked:
_cts.Cancel();
break;
case Event.BtnEnableClicked:
_cts.Dispose();
_cts = new CancellationTokenSource();
break;
}
Console.WriteLine("Event loop execution complete.");
}
}
public async Task HandleBtnAddClickedAsync(CancellationToken token)
{
try
{
await Task.Run(async () =>
{
token.ThrowIfCancellationRequested();
await Task.Delay(2000);
token.ThrowIfCancellationRequested();
Console.WriteLine("BtnAddClicked event complete");
});
}
catch (OperationCanceledException)
{
Console.WriteLine("HandleBtnAddClicked Cancelled");
}
}
public async Task HandleBtnRemoveClickedAsync(CancellationToken token)
{
try
{
await Task.Run(async () =>
{
token.ThrowIfCancellationRequested();
await Task.Delay(2000);
token.ThrowIfCancellationRequested();
Console.WriteLine("BtnRemoveClicked event complete");
});
}
catch (OperationCanceledException)
{
Console.WriteLine("HandleBtnRemoveClicked Cancelled");
}
}
And this does exactly what I want, the foreach
loop executes each Event
as fast as possible and does not get blocked. The methods that correspond to each Event
also get the convenience of try/catch
with the await Task.Run
but why does this work? Because if I simply rearrange it won't work as I want it to.
public async Task HadleEventsBlocking()
{
foreach (var event in _eventsQueue.GetConsumingEnumerable())
{
switch (event)
{
case Event.BtnAddClicked:
try
{
await Task.Run(async () =>
{
_cts.Token.ThrowIfCancellationRequested();
await Task.Delay(2000);
_cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("BtnAddClicked event complete");
});
}
catch (OperationCanceledException)
{
Console.WriteLine("HandleBtnAddClicked Cancelled");
}
break;
case Event.BtnRemoveClicked:
try
{
await Task.Run(async () =>
{
_cts.Token.ThrowIfCancellationRequested();
await Task.Delay(2000);
_cts.Token.ThrowIfCancellationRequested();
Console.WriteLine("BtnRemoveClicked event complete");
});
}
catch (OperationCanceledException)
{
Console.WriteLine("HandleBtnRemoveClicked Cancelled");
}
break;
case Event.BtnDisableClicked:
_cts.Cancel();
break;
case Event.BtnEnableClicked:
_cts.Dispose();
_cts = new CancellationTokenSource();
break;
}
Console.WriteLine("Event loop execution complete.");
}
}
Now each time an event is executed the foreach
loop is blocked by the await
inside the try/catch
and I understand why, because of the await
on the Task.Run
.
However I don't understand why I get desired behavior when I pack it into a method that I don't await
. Is it because the await
inside yields execution back to HandleEventsBlocking
and it resumes the foreach
loop? I'd also appreciate a comment on whether this is good practice, it got me far but I just don't understand the tool I'm using it and it makes me worried.