为什么调用内部带有await的方法不会阻塞父方法的执行?

问题描述 投票:0回答:1

我已经阅读了类似问题的答案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
循环吗?我也很高兴评论这是否是一个好的做法,它让我走了很远,但我只是不明白我正在使用它的工具,这让我担心。

c# asynchronous async-await
1个回答
0
投票

但是我不明白为什么当我将其打包到我不等待的方法中时会得到所需的行为。

因为它没有被等待。您可以将await 视为“异步等待”。它会暂停该方法,直到返回的任务完成。更多信息请参阅我的博客:https://blog.stephencleary.com/2012/02/async-and-await.html

我也很高兴评论这是否是一个好的做法,它让我走了很远,但我只是不明白我正在使用它的工具,这让我很担心。

绝对不是。忽略任务而不是等待它们是危险的:这意味着任何异常都会被默默地吞噬,并且您的代码无法知道处理何时完成。

更好的方法是 TPL Dataflow。或者,您可以为您的工作队列创建多个使用者。

© www.soinside.com 2019 - 2024. All rights reserved.