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
Post a Comment