What's the difference between InvokeAsync and

2019-01-22 05:51发布

问题:

I noticed in .NET 4.5 that the WPF Dispatcher had gotten a new set of methods to execute stuff on the Dispatcher's thread called InvokeAsync. Before, .NET 4.5 we had Invoke and BeginInvoke which handled this syncronously and asynchronously respectively.

Besides the naming and the slightly different overloads available, are there any major differences between the BeginInvoke and the InvokeAsync methods?

Oh, and I already checked, both can be awaited:

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

回答1:

There are no differences as the BeginInvoke method calls a private LegacyBeginInvokeImpl method which itslef calls the private method InvokeAsyncImpl (the method used by InvokeAsync). So it's basically the same thing. It seems like it's a simple refactoring, however it's strange the BeginInvoke methods weren't flagged as obsolete.

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


回答2:

There is a difference in method signature:

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

For BeginInvoke() compiler creates array Object[] implicitly while for InvokeAsync() such array is not needed:

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:

The exception handling is different.

You may want to check the following:

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


回答4:

Well, one difference I've noticed is that InvokeAsync has a generic overload that returns a DispatcherOperation as a return value and accepts a Func as its delegate input parameter. Thus, you can retrieve the result of the operation via InvokeAsync in a type-safe way analogous to how you can await the result of a Task.



回答5:

[Edit - both are same]

Thereotically

BeginInvoke works on thread on which dispatcher was created on and InvokeAsync works with thread on which dispatcher is associated.

It means if you need to process somthing based on current thread of dispatcher you will use the InvokeAsync else use BeginInvoke.

EDIT :- but above comment is meaningless as you can not change the associated thread of dispatcher once it is created.

Agree with above mention answer.. thanks