WPF分派器的InvokeAsync和BeginInvoke有什么区别


73

我在.NET 4.5中注意到,WPF Dispatcher获得了一套新的方法来在Dispatcher的线程上执行称为InvokeAsync的东西。在.NET 4.5之前,我们有InvokeBeginInvoke分别分别同步和异步处理此事件。

除了命名和可用的稍微不同的重载之外,BeginInvokeInvokeAsync方法之间是否还有重大区别?

哦,我已经检查过,都可以await编辑:

private async Task RunStuffOnUiThread(Action action)
{
    // both of these works fine
    await dispatcher.BeginInvoke(action);
    await dispatcher.InvokeAsync(action);
}

Answers:


50

两者之间没有区别,因为该BeginInvoke方法调用私有LegacyBeginInvokeImpl方法,而它的leef调用私有方法InvokeAsyncImpl(所使用的方法InvokeAsync)。所以基本上是同一回事。看起来这是一个简单的重构,但是奇怪的是BeginInvoke方法没有被标记为过时的。

BeginInvoke:

public DispatcherOperation BeginInvoke(DispatcherPriority priority, Delegate method)
{
    return this.LegacyBeginInvokeImpl(priority, method, null, 0);
}

private DispatcherOperation LegacyBeginInvokeImpl(DispatcherPriority priority, Delegate method, object args, int numArgs)
{
    Dispatcher.ValidatePriority(priority, "priority");
    if (method == null)
    {
        throw new ArgumentNullException("method");
    }
    DispatcherOperation dispatcherOperation = new DispatcherOperation(this, method, priority, args, numArgs);
    this.InvokeAsyncImpl(dispatcherOperation, CancellationToken.None);
    return dispatcherOperation;
}

InvokeAsync:

public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority)
{
    return this.InvokeAsync(callback, priority, CancellationToken.None);
}

public DispatcherOperation InvokeAsync(Action callback, DispatcherPriority priority, CancellationToken cancellationToken)
{
    if (callback == null)
    {
        throw new ArgumentNullException("callback");
    }
    Dispatcher.ValidatePriority(priority, "priority");
    DispatcherOperation dispatcherOperation = new DispatcherOperation(this, priority, callback);
    this.InvokeAsyncImpl(dispatcherOperation, cancellationToken);
    return dispatcherOperation;
}

9
目前,我正在使用BeginInvoke(按调度程序上的DispatcherUnhandledException和AppDomain.CurrentDomain.UnhandledException触发两者)按预期方式工作,但未处理的InvokeAsync异常却被默默吞下。从InvokeAsync继续执行任务以捕获异常,这似乎是一种有效的解决方法。
Lamarth 2014年

3
BeginInvoke后构图“异步编程模型”在.NET中使用BeginSomethingEndSomething方法异步操作。大概这就是为什么它未被指定为已弃用或过时的原因。除了Begin/End约定是用于使用IAsyncResult,并且BeginInvoke不使用,也不存在任何约定外EndInvoke,因此它在一开始是多余的。
sidbushes

3
从构造函数返回的DispatcherOpartion具有一个成员字段useAsyncSematics,该字段根据使用的构造函数进行设置。使用异步和等待时,这会导致异常处理方面的差异。看我的答案。
Wouter

43

异常处理是不同的。

您可能需要检查以下内容:

private async void OnClick(object sender, RoutedEventArgs e)
{
    Dispatcher.UnhandledException += OnUnhandledException;
    try
    {
        await Dispatcher.BeginInvoke((Action)(Throw));
    }
    catch
    {
        // The exception is not handled here but in the unhandled exception handler.
        MessageBox.Show("Catched BeginInvoke.");
    }

    try
    {
       await Dispatcher.InvokeAsync((Action)Throw);
    }
    catch
    {
        MessageBox.Show("Catched InvokeAsync.");
    }
}

private void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    MessageBox.Show("Catched UnhandledException");
}

private void Throw()
{
    throw new Exception();
}

11
应该将其标记为正确答案。这一事实BeginInvoke带来了下来未处理的异常与应用InvokeAsync流经返回awaitable异常背面是一个巨大的差异。
jam40jeff

1
@ jam40jeff好点。对于从BeginInvoke迁移到InvokeAsync的用户:想象一下一个巨大的旧应用程序,其中包含对BeginInvoke的大量调用。如果将其重构为使用InvokeAsync,则未处理的异常将从该异常的位置冒泡,从而降低调用线程。另一方面,BeginInvoke会将那个异常传递到主应用程序处理程序中,该处理程序将被捕获并且不会导致应用程序崩溃。无论哪种方式,它们都不是彼此的替代品,当从一个切换到另一个时,请测试行为上的差异。
Contango

您声明异常处理有所不同,并给出一个代码示例。但是你从来没有明确说明如何异常处理是不同的。如果添加,答案的质量将会提高。
JHBonarius

@JHBonarius请参阅第一个异常处理程序中的注释。
Wouter

16

方法签名有所不同:

BeginInvoke(Delegate, Object[])
InvokeAsync(Action)

对于BeginInvoke()编译器,Object[]隐式创建数组,而对于InvokeAsync()此类数组则不需要:

IL_0001:  ldarg.0
IL_0002:  call       instance class [WindowsBase]System.Windows.Threading.Dispatcher [WindowsBase]System.Windows.Threading.DispatcherObject::get_Dispatcher()
IL_0007:  ldarg.1
IL_0008:  ldc.i4.0
IL_0009:  newarr     [mscorlib]System.Object
IL_000e:  callvirt   instance class [WindowsBase]System.Windows.Threading.DispatcherOperation [WindowsBase]System.Windows.Threading.Dispatcher::BeginInvoke(class [mscorlib]System.Delegate, object[])


IL_0014:  ldarg.0
IL_0015:  call       instance class [WindowsBase]System.Windows.Threading.Dispatcher [WindowsBase]System.Windows.Threading.DispatcherObject::get_Dispatcher()
IL_001a:  ldarg.1
IL_001b:  callvirt   instance class [WindowsBase]System.Windows.Threading.DispatcherOperation [WindowsBase]System.Windows.Threading.Dispatcher::InvokeAsync(class [mscorlib]System.Action)

3

好吧,我注意到的一个区别是InvokeAsync具有泛型重载,该重载返回DispatcherOperation作为返回值,并接受Func作为其委托输入参数。因此,您可以通过InvokeAsync以类型安全的方式检索操作的结果,类似于可以等待Task结果的方式。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.