【问题标题】:await/async is still confusing [duplicate]等待/异步仍然令人困惑[重复]
【发布时间】:2020-01-31 21:01:04
【问题描述】:

在 Stephen Cleary 的文章“Async and Await”中,据说要在线程池上运行 awaitable,您需要在该 awaitable 上调用 ConfigureAwait(false)。这在某种程度上与我的经验不符。我创建了一个小应用程序,我认为它证明无需调用 ConfigureAwait 即可在单独的线程上执行 awaitable。

我使用 log4net 工具进行日志记录。 在不使用“ConfigureAwait”方法的情况下,awaitable 在不同的线程 ([3]) 上执行,然后是 UI ToolStripButton1_Click 调用(UI 线程是 [1],池线程是 [3] - 代码和日志输出附在下面)。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    public Form1()
    {
        InitializeComponent();
    }

    private async void ToolStripButton1_Click(object sender, EventArgs e)
    {
        await TestAsync();
    }
    private async Task TestAsync()
    {
        log.Info("Button clicked before Run Sleep task. Expect to run this from UI thread");
        Task t = Task.Run( () =>
        {
            log.Info("Button clicked from Run before Sleep. Expect to run this from a pool thread");
            Thread.Sleep(1000 * 5);
            log.Info("Button clicked from Run after Sleep. Expect to run this from a pool thread");
            return true;
        });//.ConfigureAwait(continueOnCapturedContext: false);
        await t;
        log.Info("Button clicked after Run. Expect to run this from UI thread"); // Expect to run this in UI thread
    }
}

}

日志输出如下:

2020-01-31 19:57:14,805 [1] 信息 WindowsFormsApp1.Form1[MoveNext] - 在运行睡眠任务之前单击了按钮。期望从 UI 线程运行它

2020-01-31 19:57:14,835 [3] 信息 WindowsFormsApp1.Form1[b__3_0] - 从睡眠前运行中单击的按钮。期望从池线程中运行它

2020-01-31 19:57:19,837 [3] 信息 WindowsFormsApp1.Form1[b__3_0] - 从“睡眠后运行”中单击的按钮。期望从池线程中运行它

2020-01-31 19:57:19,839 [1] 信息 WindowsFormsApp1.Form1[MoveNext] - 运行后单击按钮。期望从 UI 线程运行它

我没有调用 ConfigureAwait(false) 并且 awaitable 是在线程池上执行的。

【问题讨论】:

  • ConfigureAwait 是为了继续等待,它应该在哪里继续执行。
  • 默认情况下 await 捕获当前同步上下文并在其上恢复执行(在您的情况下为 UI 线程)。 ConfigureAwait(false) 禁用此功能,并在线程池上继续执行。 Task.Run 将始终在线程池线程上运行(它与 UI 线程不同)。您可能混合了异步代码执行及其恢复后的继续
  • 在我的理解中,ConfigureAwait(false) 不会影响运行 awaitable 的上下文,它会影响 continuation 的上下文(即 await 之后的所有内容) .
  • 你指的文章是8年前的。

标签: c# multithreading asynchronous async-await


【解决方案1】:

ConfigureAwait 用于继续等待,等待完成后它应该继续执行。这不是 awaitable 运行在哪个线程池上。

因此,当您的await Task.Run() 完成时,ConfigureAwait() 确定继续应该在调用上下文还是线程池上运行。

如果您将其设置为ConfigureAwait(false),那么您示例中的结局log.Info 将在与前两个​​log.Info 相同的线程池上执行

在 Stephen Cleary 的文章“Async and Await”中,据说 在需要调用的线程池上运行 awaitable ConfigureAwait(false) 在那个可等待对象上。

您没有提供文章的链接,但如果 Stephen Cleary 弄错了,我会感到非常惊讶。

【讨论】:

  • 是的。对不起链接。您指出了我所缺少的内容-“ConfigureAwait 是为了继续等待……”。非常感谢。我不认为 Stephen Cleary 错了——我试图找出我错过了什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-02
  • 2011-03-15
  • 2013-03-07
  • 1970-01-01
  • 2020-01-27
  • 1970-01-01
相关资源
最近更新 更多