C# 什么';WPF调度器的InvokeAsync和BeginInvoke之间的区别是什么

C# 什么';WPF调度器的InvokeAsync和BeginInvoke之间的区别是什么,c#,wpf,.net-4.5,async-await,C#,Wpf,.net 4.5,Async Await,我注意到在.NET4.5中,已经有了一组新的方法来执行调度程序线程上的东西,名为。在.NET4.5之前,我们有和,它分别以同步和异步方式处理这个问题 除了命名和略微不同的可用重载之外,BeginInvoke和InvokeAsync方法之间是否存在重大差异 哦,我已经检查过了,两者都可以waited: private async Task RunStuffOnUiThread(Action action) { // both of these works fine await di

我注意到在.NET4.5中,已经有了一组新的方法来执行调度程序线程上的东西,名为。在.NET4.5之前,我们有和,它分别以同步和异步方式处理这个问题

除了命名和略微不同的可用重载之外,
BeginInvoke
InvokeAsync
方法之间是否存在重大差异

哦,我已经检查过了,两者都可以
wait
ed:

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

没有区别,因为
BeginInvoke
方法调用私有
LegacyBeginInvokeImpl
方法,它调用私有方法
InvokeAsyncImpl
(由
InvokeAsync
使用的方法)。所以基本上是一样的。看起来这是一个简单的重构,但是奇怪的是,
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;
}
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;
}
InvokeAsync:

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;
}
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;
}

方法签名存在差异:

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)

异常处理是不同的

您可能需要检查以下各项:

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();
}

我注意到的一个区别是,InvokeAsync有一个通用重载,它将dispatcherooperation作为返回值返回,并接受Func作为其委托输入参数。因此,您可以通过InvokeAsync以一种类型安全的方式检索操作结果,类似于等待任务结果的方式。

目前,我正在使用BeginInvoke(触发dispatcher上的DispatcherUnhandledException和AppDomain.CurrentDomain.UnhandledException)使未处理的异常按预期工作,但InvokeAsync上未处理的异常正在被默默地吞噬。从InvokeAsync继续执行任务并捕获异常似乎是一种有效的解决方法。
BeginInvoke
是在.NET中使用
BeginMething
EndSomething
方法进行异步操作的模式。这大概就是为什么它没有被指定为弃用或过时。除了
Begin
/
End
约定用于使用
IAsyncResult
BeginInvoke
没有,也没有任何
EndInvoke
,从构造函数返回的DispatcherPartion有一个memberfield UseAyncSematics,它是基于所使用的构造函数设置的。这会导致使用async和Wait时异常处理的不同。请看我的答案。这应该被标记为正确答案。事实上,
BeginInvoke
在未处理的异常情况下关闭应用程序,而
InvokeAsync
通过返回的等待返回异常,这是一个巨大的区别。@jam40jeff说得好。对于那些从BeginInvoke迁移到InvokeAsync的人来说:想象一下一个巨大的遗留应用程序,其中包含大量对BeginInvoke的调用。如果将其重构为使用InvokeAsync,则未处理的异常将从异常点冒出,从而导致调用线程停止。另一方面,BeginInvoke会将该异常传递到主应用程序处理程序,主应用程序处理程序会被捕获,并且不会使应用程序停机。无论哪种方式,它们都不是彼此的插入式替换,当从一个切换到另一个时,测试行为的差异。您声明异常处理是不同的,并给出一个代码示例。但您从未明确说明异常处理是如何不同的。如果您添加该选项,答案的质量将会提高。@JHBonarius将在第一个异常处理程序中看到注释。