代码之家  ›  专栏  ›  技术社区  ›  Thern

在UI线程(WPF)上的void方法中调用异步函数

  •  0
  • Thern  · 技术社区  · 1 年前

    我有以下问题:我需要将一个异步方法绑定到Window。闭幕式。由于Closing事件需要一个具有void签名的CancelEventHandler,所以我的WindowClosing方法不能是Task类型。所以我有这个:

    public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        DeinitializeThisFirst();
        DeinitializeThisAfterwards();
    }
    

    由于硬件原因,在调用DeinitializeHisAfterwards()之前,DeinitialiseHisFirst()必须完成。但是DeinitializeHisFirst()是一个不可行的任务。由于我无法将Window_Closing的签名更改为Task,因此无法执行以下操作:

    public async Task Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        await DeinitializeThisFirst();
        DeinitializeThisAfterwards();
    }
    

    由于我在UI线程上,这将导致死锁:

    public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        DeinitializeThisFirst().ConfigureAwait(false).GetAwaiter().GetResult();
        DeinitializeThisAfterwards();
    }
    

    在单独的Task中执行它将导致DeinitializeHisAfterwards()提前执行:

    public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        Task.Run(async () => await DeinitializeThisFirst());
        DeinitializeThisAfterwards();
    }
    

    将其包装在ContinueWith()中将防止两者都被执行,因为窗口会立即关闭:

    public void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        DeinitializeThisFirst().ContinueWith(_ =>
        {
            DeinitializeThisAfterwards();
        });
    }
    

    我在这里不知所措。有人能帮忙吗?

    1 回复  |  直到 1 年前
        1
  •  2
  •   Theodor Zoulias    1 年前

    你必须 Cancel 最初的 Closing 的,以及 Close 稍后当取消初始化完成时以编程方式进行。下面的例子应该让你知道该怎么做:

    private bool _deinitializeRunning = false;
    private bool _deinitializeFinished = false;
    
    public async void Window_Closing(object sender, CancelEventArgs e)
    {
        if (!_deinitializeFinished) e.Cancel = true;
        if (_deinitializeRunning) return;
        _deinitializeRunning = true;
        await DeinitializeThisFirst();
        DeinitializeThisAfterwards();
        _deinitializeRunning = false;
        _deinitializeFinished = true;
        await Task.Yield(); // Ensure that the Close is called asynchronously
        Close();
    }
    

    我省略了 try / finally 应包含 Deinitialize 调用,这样在出现错误时不会永远阻止窗口关闭。