【问题标题】:await inside a WPF Dispatcher call在 WPF 调度程序调用中等待
【发布时间】:2020-02-27 04:21:18
【问题描述】:

在传递给调度程序的委托中等待时确保错误处理的最佳解决方案是什么?

简而言之:我需要在将计算解析为 Foo 后返回一个数字。该计算非常慢,必须在 UI 线程上完成。计算必须以Task<string> 的形式交给另一个类。

我能想出的唯一解决方案是 CallFooWithATask___3 - 甚至不确定..

public class Foo
{
    public void CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(Task<string> task) { }
}

// CallFooWithATask___ is invoked from unknown thread. Can't wait for GetWithSideEffects on calling thread
public class SomeClass
{
    private TextBox textBox;

    public int CallFooWithATask___1(Foo foo)
    {
        // not good - async void -> no error handling
        var tcs = new TaskCompletionSource<string>();
        Dispatcher.CurrentDispatcher.BeginInvoke(async () =>
        {                
            var a = await GetWithSideEffects();
            textBox.Text = a;
            tcs.SetResult(a);
        });

        // quite fast - probally put it in a queue and returns
        foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(tcs.Task);

        return 1;
    }

    public async Task<int> CallFooWithATask___2(Foo foo)
    {
        // not good - still async void  -> no error handling .. when is foo actually called? I assume when hitting the inner await'ish?
        var task =  await Dispatcher.CurrentDispatcher.InvokeAsync(async () =>
        {
            var a = await GetWithSideEffects();
            textBox.Text = a;
            return a;
        });

        // quite fast - probally put it in a queue and returns
        foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(task);

        return 1;
    }

    public int CallFooWithATask___3(Foo foo)
    {
        // what is the elegant solution - probally not this?
        var tcs = new TaskCompletionSource<string>();
        Dispatcher.CurrentDispatcher.BeginInvoke(async () =>
        {
            try
            {
                var a = await GetWithSideEffects();
                textBox.Text = a;
                tcs.SetResult(a);
            }
            catch (Exception ex) { tcs.SetException(ex); }
        });

        // quite fast - probally put it in a queue and returns
        foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(tcs.Task);

        return 1;
    }

    // this might trigger ui updates and is very slow ..
    private Task<string> GetWithSideEffects()=> Task.FromResult("42");
}

【问题讨论】:

  • 你的问题是什么?
  • 在传递给调度程序的委托中等待时确保错误处理的最佳解决方案是什么。
  • 一般来说,我建议结构化代码以便 UI 调用逻辑,而不是逻辑调用 UI。 UI 不应该为逻辑提供服务。如果逻辑需要服务,则为其定义一个接口,然后 UI 可以实现该接口,如果绝对必要,使用SynchronizationContext

标签: c# wpf async-await


【解决方案1】:

你已经很接近了。只需在方法或Func&lt;Task&lt;string&gt;&gt; 中提取您的异步代码,以避免以async void 结尾:

Func<Task<string>> func = async () =>
{
    var a = await GetWithSideEffects();

    return a;
};

然后使用InvokeAsync 调用它。你最终会得到一个Task&lt;Task&lt;string&gt;&gt;。内部任务是您的 async 方法返回的任务,外部任务是由InvokeAsync 生成的任务,用于指示实际调度调用的时间。 使用.Unwrap 合并这些任务,最后将它们发送到您的其他方法:

var task = Dispatcher.InvokeAsync(func).Task.Unwrap();

foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(task);

【讨论】:

    猜你喜欢
    • 2021-09-04
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 1970-01-01
    • 2019-03-25
    • 2015-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多