Search code examples
wpfdispatcherui-thread

Is checking the Thread is "UI Thread" before Calling Dispatcher.Invoke redundant?


In regards to

Application.Current.Dispatcher.Invoke(action);

I have looked at CheckAccess() and various ways of determining whether i'm on the main UI thread. Though after looking at the Dispatcher Source code for Invoke, it seems to call CheckAccess() and performs other checks anyway

Invoke Source Code

public void Invoke(Action callback, DispatcherPriority priority, CancellationToken cancellationToken, TimeSpan timeout)
{
   ...
   ...

   // Fast-Path: if on the same thread, and invoking at Send priority,
   // and the cancellation token is not already canceled, then just
   // call the callback directly.
   if (!cancellationToken.IsCancellationRequested && priority == DispatcherPriority.Send && CheckAccess())
   {
      SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current;

      try
      {
         DispatcherSynchronizationContext newSynchronizationContext;
         if (BaseCompatibilityPreferences.GetReuseDispatcherSynchronizationContextInstance())
         {
            newSynchronizationContext = _defaultDispatcherSynchronizationContext;
         }
         else
         {
            if (BaseCompatibilityPreferences.GetFlowDispatcherSynchronizationContextPriority())
            {
               newSynchronizationContext = new DispatcherSynchronizationContext(this, priority);
            }
            else
            {
               newSynchronizationContext = new DispatcherSynchronizationContext(this, DispatcherPriority.Normal);
            }
         }
         SynchronizationContext.SetSynchronizationContext(newSynchronizationContext);

         callback();
         ...

So the most reliable way to check whether my dialog needs to be invoked is by calling Invoke? looking at CheckAccess and SynchronisationContexts solutions when i dont have access to a Control seem to be redundant.

Is this the case or is there some edge cases i'm missing, or hidden performance hit i cant see?


Solution

  • I guess it depends.

    If you're mainly after correctness and/or code brevity, then yes, the call is redundant - calling

    Dispatcher.Invoke(action);
    

    will be functionally equivalent1 to

    if(Dispatcher.CheckAccess())
        action();
    else
        Dispatcher.Invoke(action);
    

    If however your concern is performance, then it's not so obvious. CheckAccess literally reads

    return Thread == Thread.CurrentThread;
    

    so even if it's called twice as many times, it will hardly be noticeable. Dispatcher.Invoke however does some additional work, such as argument checking and, possibly, swapping synchronization context, so I guess it potentially has larger overhead than a redundant call to CheckAccess(). But, as usual with performance optimization, there's no single right answer - it depends on your particular case (for example on the likelihood of this code being called from non-UI thread).


    1 Obviously there may be additional things happening with the synchronization context when calling Dispatcher.Invoke, but unless action is making use of it, the result will be the same