如何摆脱 Dispatcher.Invoke






4.82/5 (7投票s)
使用 Task 切换上下文
引言
在使用 Dispatcher
在线程之间切换上下文时遇到困难后,我很高兴能够通过使用 Task
来传达以下解决方案。
使用代码
我将任务包装在一个接受 Action
的执行方法中。
//
// private method to switch context.
//
private void Execute(Action action)
{
try
{
// Invoke action on UI thread
var task = Task.Factory.StartNew(() =>
{
if (action != null)
action();
}, CancellationToken.None, TaskCreationOptions.None, taskScheduler);
if (task.IsFaulted)
throw new AggregateException(task.Exception);
}
catch (Exception e)
{
if (Debugger.IsAttached) Debugger.Break();
// Handle exception here.
}
}
要使用该方法,需要在 UI 线程内设置任务调度器,并且必须设置 SynchronizationContext
。
// Must be set on the UI thread
taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
// Example
// Invoke event LogonStatusChanged on the UI thread.
private void InvokeLoginStatusChanged(Object sender, EventArgs e)
{
if (LogonStatusChanged == null) return;
Execute(() => LogonStatusChanged(this, e));
}
关注点
当涉及到单元测试时,使用 Task
在线程之间切换上下文会很棘手。 在这些情况下,您可以使用以下代码创建自己的同步上下文。
// Creating you own Synchronization context
[TestInitialize]
private void TestSetup()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
}
请注意,同步上下文在应用程序启动时不会立即创建。
如果在 App.cs 构造函数中引用 TaskScheduler.FromCurrentSynchronizationContext()
,它将始终为 null
。
相反,将任何初始化移动到 OnStartup
。
protected override void OnStartup(StartupEventArgs e)
{
// Initialize here
base.OnStartup(e);
}
历史
2013-12-6:初始版本。
2013-12-8:添加了一个示例。