我刚刚注意到,对于 .NET 4.5,每个Dispatcher.BeginInvoke
/InvokeAsync
回调都是在其自己非常独特的同步上下文(的实例DispatcherSynchronizationContext
)上执行的。这种变化背后的原因是什么?
以下简单的 WPF 应用程序说明了这一点:
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 i = 0;
test = () =>
{
var sc = SynchronizationContext.Current;
Dispatcher.CurrentDispatcher.InvokeAsync(() =>
{
Debug.Print("same context #" + i + ": " +
(sc == SynchronizationContext.Current));
if ( i < 10 )
{
i++;
test();
}
});
};
this.Loaded += (s, e) => test();
}
}
}
输出:
相同的上下文#0:错误 相同的上下文#1:错误 相同的上下文#2:错误 ...
设置BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance
为true
恢复 .NET 4.0 行为:
public partial class App : Application
{
static App()
{
BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance = true;
}
}
相同的上下文#0:真 相同的上下文#1:真 相同的上下文#2:真 ...
研究.NET 源代码表明DispatcherOperation
:
[SecurityCritical]
private void InvokeImpl()
{
SynchronizationContext oldSynchronizationContext = SynchronizationContext.Current;
try
{
// We are executing under the "foreign" execution context, but the
// SynchronizationContext must be for the correct dispatcher.
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(_dispatcher));
// Invoke the delegate that does the work for this operation.
_result = _dispatcher.WrappedInvoke(_method, _args, _isSingleParameter);
}
finally
{
SynchronizationContext.SetSynchronizationContext(oldSynchronizationContext);
}
}
我不明白为什么可能需要这样做,无论如何排队的回调Dispatcher.BeginInvoke
/InvokeAsync
都在正确的线程上执行,该线程上已经DispatcherSynchronizationContext
安装了一个实例。
此更改的一个有趣的副作用是.NET 4.5 WPF 中await TaskCompletionSource.Task
的延续(由 触发TaskCompletionSource.SetResult
)几乎总是异步的,这与 WinForms 或 v4.0 WPF 不同(更多细节)。