Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/259.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 未配置的等待等待(false)在另一个线程上继续_C#_.net_Winforms_Async Await - Fatal编程技术网

C# 未配置的等待等待(false)在另一个线程上继续

C# 未配置的等待等待(false)在另一个线程上继续,c#,.net,winforms,async-await,C#,.net,Winforms,Async Await,我有一个WinForms应用程序,我有一些需要在UI线程上运行的代码。但是,wait之后的代码在不同的线程上运行 protected override async void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); // This runs on the UI thread. mainContainer.Controls.Clear(); var result = await DoSomet

我有一个WinForms应用程序,我有一些需要在UI线程上运行的代码。但是,
wait
之后的代码在不同的线程上运行

protected override async void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);

    // This runs on the UI thread.
    mainContainer.Controls.Clear();

    var result = await DoSomethingAsync();

    // This also needs to run on the UI thread, but it does not.
    // Instead it throws an exception:
    // "Cross-thread operation not valid: Control 'mainContainer' accessed from a thread other than the thread it was created on"
    mainContainer.Controls.Add(new Control());
}
我还尝试显式添加
ConfigureAwait(true)
,但没有任何区别。我的理解是,如果省略了
ConfigureAwait(false)
,那么继续应该在原始线程上运行。在某些情况下这是不正确的吗

我还注意到,如果我在wait之前向集合添加一个控件,那么continuation会神奇地在正确的线程上运行

protected override async void OnHandleCreated(EventArgs e)
{
    base.OnHandleCreated(e);

    // This runs on the UI thread.
    mainContainer.Controls.Add(new Control());
    mainContainer.Controls.Clear();

    var result = await DoSomethingAsync();

    // This also runs on the UI thread now. Why?
    mainContainer.Controls.Add(new Control());
}
我的问题是:

  • 为什么会这样
  • 我如何说服continuation在UI线程上运行(理想情况下,不必进行添加和删除控件的操作)
  • 以下是
    DoSomethingAsync
    的重要部分供参考。它使用RestSharp提交HTTP请求

    protected async Task DoSomethingAsync()
    {
        IRestRequest request = CreateRestRequest();
    
        // Here I await the response from RestSharp.
        // Client is an IRestClient instance.
        // I have tried removing the ConfigureAwait(false) part, but it makes no difference.
        var response = await Client.ExecuteTaskAsync(request).ConfigureAwait(false);
    
        if (response.ResponseStatus == ResponseStatus.Error)
            throw new Exception(response.ErrorMessage ?? "The request did not complete successfully.");
    
        if (response.StatusCode >= HttpStatusCode.BadRequest)
            throw new Exception("Server responded with an error: " + response.StatusCode);
    
        // I also do some processing of the response here; omitted for brevity.
        // There are no more awaits.
    }
    
    似乎在
    OnHandleCreated
    上发生了一些奇怪的事情。我的解决方案是使用
    OnLoad
    。我对这个解决方案非常满意,因为在我的情况下没有理由使用
    OnHandleCreated

    我仍然很好奇为什么会发生这种情况,所以如果有人知道,请随意发布另一个答案

    编辑:

    我发现了真正的问题:原来我是在
    ConfigureAwait(false)
    之后调用
    Form.ShowDialog()
    。因此,表单是在UI线程上构建的,但随后我在非UI线程上调用了
    ShowDialog
    。我很惊讶这居然奏效了

    我已经删除了
    ConfigureAwait(false)
    ,因此现在在UI线程上调用了
    ShowDialog

    我的理解是,如果省略ConfigureAwait(false),那么continuation应该在原始线程上运行。在某些情况下这是不正确的吗

    实际上,默认情况下,
    await
    将捕获当前上下文,并使用此上下文恢复
    async
    方法。此上下文是
    SynchronizationContext.Current
    ,除非它是
    null
    ,在这种情况下,它是
    TaskScheduler.Current
    (通常是线程池上下文)。大多数时候,UI线程都有一个UI
    SynchronizationContext
    ——对于WinForms,它是
    WinFormsSynchronizationContext
    的一个实例

    我还注意到,如果我在wait之前向集合添加一个控件,那么continuation会神奇地在正确的线程上运行

    protected override async void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
    
        // This runs on the UI thread.
        mainContainer.Controls.Add(new Control());
        mainContainer.Controls.Clear();
    
        var result = await DoSomethingAsync();
    
        // This also runs on the UI thread now. Why?
        mainContainer.Controls.Add(new Control());
    }
    
    没有线程自动以同步上下文开始。WinForms
    SynchronizationContext
    在创建第一个控件时按需安装。这就是为什么在创建控件后,它会在UI线程上恢复


    由于移动到
    OnLoad
    是一个可行的解决方案,我建议您还是这样做吧。唯一的其他选项(在创建控件之前在UI线程上恢复)是在第一次
    wait

    之前手动创建控件。为什么您认为这会在不同的线程上运行?这段代码在哪里运行?事件处理程序?我知道它在另一个线程上运行,因为等待后的代码引发异常:“跨线程操作无效:从创建它的线程以外的线程访问控件“mainContainer”。是的,代码在事件处理程序中运行。我将更新我的问题。
    OnHandleCreated
    注册到哪个事件?因为winforms中的事件处理程序通常有两个参数。
    HandleCreated
    事件肯定涉及到黑魔法。我切换到使用
    Load
    事件,一切正常。无论如何,我不知道为什么要使用
    HandleCreated
    。(我从其他人那里继承了这段代码,很多代码都很凌乱)。你能把
    Debug.WriteLine(new{SynchronizationContext.Current})
    放在
    await DoSomethingAsync()
    前面的
    OnHandleCreated
    中吗?它是否显示
    System.Windows.Forms.WindowsFormsSynchronizationContext
    (预期)或
    System.Threading.SynchronizationContext
    ?我在等待之前得到
    System.Threading.SynchronizationContext
    ,在等待之后得到null。我不确定昨天为什么会得到一个非空值。但我想这就是原因。我还注意到,如果我在repo的不同分支上的同一项目中运行相同的代码,我会得到一个
    System.Windows.Forms.windowsformsssynchronizationcontext
    ,并且一切正常。我想知道这是否与表单初始化的方式有关。在您最近的编辑中,您应该将您的答案标记为已接受,因为它解释了实际发生的情况。否则可以在
    OnHandleCreated
    中使用
    wait
    ,查看我对Stephen的回答。@Noseratio是的,我必须等待两天才能接受我自己的回答。我会接受它一次,所以让我:)实际上,
    WindowsFormsSynchronizationContext
    安装在
    Control
    的构造函数中,所以调用
    OnHandleCreated
    时它应该已经存在了。这肯定是另外一回事。这可能是相关的,但在这种情况下,
    OnHandleCreated
    OnLoad
    都会受到影响。