我已经阅读了类似问题的答案https://stackoverflow.com/a/43841624/11478903但它没有解释执行的产生。
我有一个线程使用
GetConsumingEnumerable
属性中的 BlockingCollection<Event> _eventQueue
来消耗事件。
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");
}
}
}
这正是我想要的,
foreach
循环尽可能快地执行每个Event
并且不会被阻塞。与每个 Event
对应的方法也可以通过 try/catch
获得 await Task.Run
的便利,但是为什么会这样呢?因为如果我简单地重新排列它就不会像我想要的那样工作。
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.");
}
}
现在,每次执行事件时,
foreach
循环都会被await
内的try/catch
阻塞,我明白为什么,因为await
上的Task.Run
。
但是我不明白为什么当我将其打包到我不喜欢的方法中时会得到所需的行为
await
。是因为内部的await
将执行返回到HandleEventsBlocking
并恢复了foreach
循环吗?我也很高兴评论这是否是一个好的做法,它让我走了很远,但我只是不明白我正在使用它的工具,这让我担心。
但是我不明白为什么当我将其打包到我不等待的方法中时会得到所需的行为。
因为它没有被等待。您可以将await 视为“异步等待”。它会暂停该方法,直到返回的任务完成。更多信息请参阅我的博客:https://blog.stephencleary.com/2012/02/async-and-await.html
我也很高兴评论这是否是一个好的做法,它让我走了很远,但我只是不明白我正在使用它的工具,这让我很担心。
绝对不是。忽略任务而不是等待它们是危险的:这意味着任何异常都会被默默地吞噬,并且您的代码无法知道处理何时完成。
更好的方法是 TPL Dataflow。或者,您可以为您的工作队列创建多个使用者。