c# – 为什么’await’改变WinForms program.cs中的线程上下文

使用await调用异步方法的正常行为是它的上下文保持不变,如下例所示:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private async void Form1_Load(object sender, EventArgs e)
    {
        //main thread here
        await SampleAsync();
        //stays as main thread
        var g = 10;
    }

    static async Task SampleAsync()
    {
        await Task.Delay(1000);
    }
}

但是当我在Winforms主控制台方法调用中使用它时,这种行为不再成立:在调用await方法之后,线程从主线程更改为工作线程.为什么是这样?我希望它在等待调用后保持在Main(UI线程)上.

    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        MainAsync().Wait();
    }

    static async Task MainAsync()
    {
        Application.Run(new Form1());
        //Main Thread here
        await Task.Delay(1000);
        //Why does it turn in to worker thread here? ConfigureAwait(false) is not used here?!
        var g = 5;
        //Run other Application.Run() if certain conditions apply; but I need to be in the Main Thread.
    }

如果你想知道我想要实现什么,我试图捕获异步方法中的错误,MainAsync,而不是Main,以便我可以避免从AggregateException(https://msdn.microsoft.com/en-us/magazine/JJ991977.aspx;图4)中解开错误.我还想留在UI线程中,这样我就可以在UI线程中运行其他Application.Run.

最佳答案 因为Application.Run是“运行”主线程上的延续,所以一旦Application.Run返回SynchronizationContext.Current,则为“UI线程”取消设置,因此await必须使用默认上下文,即ThreadPool.

因为此时没有UI,我建议在此时进行同步等待(.Wait())而不是await,这将确保您仍然在以后的Application.Run调用的同一个STA线程上.

编辑:没有看到你提到关于解开AggregateException,所有等待是用AggregateException做的相当于

catch(AggregateException ex)
{
    ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
}

这导致重新引发AggregateException的第一个异常,而不会弄乱它的堆栈跟踪.

点赞