c# - Why a unique synchronization context for each Dispatcher.BeginInvoke callback? -


i've noticed .net 4.5 each dispatcher.begininvoke/invokeasync callback executed on own unique synchronization context (an instance of dispatchersynchronizationcontext). what's reason behind change?

the following trivial wpf app illustrates this:

using system; using system.diagnostics; using system.threading; using system.windows; using system.windows.threading;  namespace wpfapplication {     public partial class mainwindow : window     {         public mainwindow()         {             initializecomponent();              action test = null;             var = 0;              test = () =>             {                 var sc = synchronizationcontext.current;                  dispatcher.currentdispatcher.invokeasync(() =>                  {                     debug.print("same context #" + + ": " +                         (sc == synchronizationcontext.current));                     if ( < 10 )                      {                         i++;                         test();                     }                 });             };              this.loaded += (s, e) => test();         }     } } 

output:

 same context #0: false same context #1: false same context #2: false ... 

setting basecompatibilitypreferences.reusedispatchersynchronizationcontextinstance true restores .net 4.0 behavior:

public partial class app : application {     static app()     {         basecompatibilitypreferences.reusedispatchersynchronizationcontextinstance = true;     } } 
 same context #0: true same context #1: true same context #2: true ... 

studying the .net sources dispatcheroperation shows this:

[securitycritical] private void invokeimpl()  {     synchronizationcontext oldsynchronizationcontext = synchronizationcontext.current;      try      {         // executing under "foreign" execution context,          // synchronizationcontext must correct dispatcher.          synchronizationcontext.setsynchronizationcontext(new dispatchersynchronizationcontext(_dispatcher));          // invoke delegate work operation.         _result = _dispatcher.wrappedinvoke(_method, _args, _issingleparameter);     }          {         synchronizationcontext.setsynchronizationcontext(oldsynchronizationcontext);      }  } 

i don't understand why might needed, callbacks queued dispatcher.begininvoke/invokeasync anyway executed on correct thread has instance of dispatchersynchronizationcontext installed on it.

one interesting side effect of change await taskcompletionsource.task continuation (triggered taskcompletionsource.setresult) asynchronous in .net 4.5 wpf, unlike winforms or v4.0 wpf (some more details).

it explained long comment in source code. quoting 4.5.1 reference source in wpf\src\base\system\windows\basecompatibilitypreferences.cs:

    ///     wpf 4.0 had performance optimization     ///     reuse same instance of     ///     dispatchersynchronizationcontext when preparing     ///     executioncontext invoking dispatcheroperation.      ///     had observable impacts on behavior.     ///     ///     1) task-parallel implementations check reference     ///         equality of synchronizationcontext determine if     ///         completion can inlined - significant performance win.     ///     ///     2) but, executioncontext flow     ///         synchronizationcontext result in same     ///         instance of dispatchersynchronizationcontext being     ///         current synchronizationcontext on 2 different threads.     ///         continuations inlined, resulting in code     ///         running on wrong thread.     ///     ///     in 4.5 changed behavior use new instance of     ///     dispatchersynchronizationcontext every operation, ,     ///     whenever synchronizationcontext.createcopy called - such     ///     when executioncontext being flowed thread.     ///     has own observable impacts:     ///     ///     1) task-parallel implementations check reference     ///         equality of synchronizationcontext determine if     ///         completion can inlined - since instances     ///         different, causes them resort slower     ///         path potentially cross-thread completions.     ///     ///     2) task-parallel implementations implement potentially     ///         cross-thread completions callling     ///         synchronizationcontext.post , wait() , event     ///         signaled.  if not true cross-thread completion,     ///         rather 2 seperate instances of     ///         dispatchersynchronizationcontext same thread,     ///         result in deadlock. 

or put way, fixed bug in code :)


Comments

Popular posts from this blog

c# - How to get the current UAC mode -

postgresql - Lazarus + Postgres: incomplete startup packet -

javascript - Ajax jqXHR.status==0 fix error -