我正在使用Dispatcher
这样从外部切换到UI线程
Application.Current.Dispatcher.Invoke(myAction);
Run Code Online (Sandbox Code Playgroud)
但我在一些论坛上看到人们建议使用Synchronization
上下文而不是Dispatcher
.
SynchronizationContext.Current.Post(myAction,null);
Run Code Online (Sandbox Code Playgroud)
它们之间有什么区别,为什么SynchronizationContext
要使用它?
Dir*_*irk 27
它们都有类似的效果,但SynchronizationContext
更通用.
Application.Current.Dispatcher
指的是应用程序的WPF调度程序,并使用Invoke
on执行该应用程序主线程上的委托.
SynchronizationContext.Current
另一方面,根据当前线程返回不同的实现.当在WPF应用程序的UI线程上调用时,它返回一个SynchronizationContext
使用调度程序的程序,当在WinForms应用程序的UI线程上调用它时,它返回另一个.
您可以SynchronizationContext
在其MSDN文档中看到继承的类:WindowsFormsSynchronizationContext和DispatcherSynchronizationContext.
使用时需要注意的一点SynchronizationContext
是它返回当前线程的同步上下文.如果要使用另一个线程的同步上下文,例如UI线程,则必须首先获取其上下文并将其存储在变量中:
public void Control_Event(object sender, EventArgs e)
{
var uiContext = SynchronizationContext.Current;
Task.Run(() =>
{
// do some work
uiContext.Post(/* update UI controls*/);
}
}
Run Code Online (Sandbox Code Playgroud)
这不适用于Application.Current.Dispatcher
,它始终返回应用程序的调度程序.
Moe*_*bai 21
使用时WPF
,SynchronizationContext.Current
对象的类型DispatcherSynchronizationContext
实际上只是Dispatcher
对象周围的包装,Post
而Send
方法只是委托给Dispatcher.BeginInvoke
和Dispatcher.Invoke
.
所以即使你决定使用SynchronizationContext
我认为你最终会在幕后调用调度员.
此外,我认为使用它有点麻烦,SynchronizationContext
因为您必须将对当前上下文的引用传递给需要调用UI的所有线程.
Sta*_*itz 12
虽然已经指出了差异,但我并没有真正看到在这里明确阐述的选择一个而不是另一个的理由。因此,也许这将有助于解释SynchronizationContext对象首先要解决的问题:
因此,要回答您选择哪个问题的问题,仅从上述标准看来,使用SynchronizationContext比使用Dispatcher更可取。
但是,这样做还有更多令人信服的理由:
通过使用SynchronizationContext处理UI线程上的执行代码,您现在可以通过解耦的接口轻松地将操作与显示分离。导致下一点:
如果您曾经尝试模拟与Dispatcher和SynchronizationContext一样复杂的对象,而该对象要处理的方法要少得多,那么您很快就会体会到SynchronizationContext提供的简单得多的接口。
如您所见,SynchronizationContext是在许多UI框架中实现的:WinForms,WPF,ASP.NET等。如果编写代码以与一组API接口,则代码将变得更加可移植且易于维护,并且测试。
您甚至不需要注入上下文对象……您可以通过接口匹配上下文对象上的方法(包括代理)来注入任何对象。
举例来说:
注意:我省略了异常处理以使代码清晰可见。
假设我们有一个只有一个按钮的WPF应用程序。单击该按钮后,您将启动与UI更新交错的异步工作任务的漫长过程,并且需要在两者之间协调IPC。
使用WPF和传统的Dispatch方法,您可以编写如下代码:
/// <summary>
/// Start a long series of asynchronous tasks using the Dispatcher for coordinating
/// UI updates.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Start_Via_Dispatcher_OnClick(object sender, RoutedEventArgs e)
{
// update initial start time and task status
Time_Dispatcher.Text = DateTime.Now.ToString("hh:mm:ss");
Status_Dispatcher.Text = "Started";
// create UI dont event object
var uiUpdateDone = new ManualResetEvent(false);
// Start a new task (this uses the default TaskScheduler,
// so it will run on a ThreadPool thread).
Task.Factory.StartNew(async () =>
{
// We are running on a ThreadPool thread here.
// Do some work.
await Task.Delay(2000);
// Report progress to the UI.
Application.Current.Dispatcher.Invoke(() =>
{
Time_Dispatcher.Text = DateTime.Now.ToString("hh:mm:ss");
// signal that update is complete
uiUpdateDone.Set();
});
// wait for UI thread to complete and reset event object
uiUpdateDone.WaitOne();
uiUpdateDone.Reset();
// Do some work.
await Task.Delay(2000); // Do some work.
// Report progress to the UI.
Application.Current.Dispatcher.Invoke(() =>
{
Time_Dispatcher.Text = DateTime.Now.ToString("hh:mm:ss");
// signal that update is complete
uiUpdateDone.Set();
});
// wait for UI thread to complete and reset event object
uiUpdateDone.WaitOne();
uiUpdateDone.Reset();
// Do some work.
await Task.Delay(2000); // Do some work.
// Report progress to the UI.
Application.Current.Dispatcher.Invoke(() =>
{
Time_Dispatcher.Text = DateTime.Now.ToString("hh:mm:ss");
// signal that update is complete
uiUpdateDone.Set();
});
// wait for UI thread to complete and reset event object
uiUpdateDone.WaitOne();
uiUpdateDone.Reset();
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult()
.ContinueWith(_ =>
{
Application.Current.Dispatcher.Invoke(() =>
{
Status_Dispatcher.Text = "Finished";
// dispose of event object
uiUpdateDone.Dispose();
});
});
}
Run Code Online (Sandbox Code Playgroud)
该代码按预期工作,但具有以下缺点:
现在,让我们使用SynchronizationContext对象再试一次:
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Start_Via_SynchronizationContext_OnClick(object sender, RoutedEventArgs e)
{
// update initial time and task status
Time_SynchronizationContext.Text = DateTime.Now.ToString("hh:mm:ss");
Status_SynchronizationContext.Text = "Started";
// capture synchronization context
var sc = SynchronizationContext.Current;
// Start a new task (this uses the default TaskScheduler,
// so it will run on a ThreadPool thread).
Task.Factory.StartNew(async () =>
{
// We are running on a ThreadPool thread here.
// Do some work.
await Task.Delay(2000);
// Report progress to the UI.
sc.Send(state =>
{
Time_SynchronizationContext.Text = DateTime.Now.ToString("hh:mm:ss");
}, null);
// Do some work.
await Task.Delay(2000);
// Report progress to the UI.
sc.Send(state =>
{
Time_SynchronizationContext.Text = DateTime.Now.ToString("hh:mm:ss");
}, null);
// Do some work.
await Task.Delay(2000);
// Report progress to the UI.
sc.Send(state =>
{
Time_SynchronizationContext.Text = DateTime.Now.ToString("hh:mm:ss");
}, null);
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult()
.ContinueWith(_ =>
{
sc.Post(state =>
{
Status_SynchronizationContext.Text = "Finished";
}, null);
});
}
Run Code Online (Sandbox Code Playgroud)
请注意,这一次,我们不需要依赖外部对象在线程之间进行同步。实际上,我们正在上下文之间进行同步。
现在,即使您没有要求,但出于完整性考虑,还有另一种方法可以抽象地完成您想要的事情,而无需SynchronizationContext对象或使用Dispatcher。由于我们已经在使用TPL(任务并行库)进行任务处理,因此我们可以按照以下方式使用任务调度程序:
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Start_Via_TaskScheduler_OnClick(object sender, RoutedEventArgs e)
{
Time_TaskScheduler.Text = DateTime.Now.ToString("hh:mm:ss");
// This TaskScheduler captures SynchronizationContext.Current.
var taskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Status_TaskScheduler.Text = "Started";
// Start a new task (this uses the default TaskScheduler,
// so it will run on a ThreadPool thread).
Task.Factory.StartNew(async () =>
{
// We are running on a ThreadPool thread here.
// Do some work.
await Task.Delay(2000);
// Report progress to the UI.
var reportProgressTask = ReportProgressTask(taskScheduler, () =>
{
Time_TaskScheduler.Text = DateTime.Now.ToString("hh:mm:ss");
return 90;
});
// get result from UI thread
var result = reportProgressTask.Result;
Debug.WriteLine(result);
// Do some work.
await Task.Delay(2000); // Do some work.
// Report progress to the UI.
reportProgressTask = ReportProgressTask(taskScheduler, () =>
{
Time_TaskScheduler.Text = DateTime.Now.ToString("hh:mm:ss");
return 10;
});
// get result from UI thread
result = reportProgressTask.Result;
Debug.WriteLine(result);
// Do some work.
await Task.Delay(2000); // Do some work.
// Report progress to the UI.
reportProgressTask = ReportProgressTask(taskScheduler, () =>
{
Time_TaskScheduler.Text = DateTime.Now.ToString("hh:mm:ss");
return 340;
});
// get result from UI thread
result = reportProgressTask.Result;
Debug.WriteLine(result);
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default)
.ConfigureAwait(false)
.GetAwaiter()
.GetResult()
.ContinueWith(_ =>
{
var reportProgressTask = ReportProgressTask(taskScheduler, () =>
{
Status_TaskScheduler.Text = "Finished";
return 0;
});
reportProgressTask.Wait();
});
}
/// <summary>
///
/// </summary>
/// <param name="taskScheduler"></param>
/// <param name="func"></param>
/// <returns></returns>
private Task<int> ReportProgressTask(TaskScheduler taskScheduler, Func<int> func)
{
var reportProgressTask = Task.Factory.StartNew(func,
CancellationToken.None,
TaskCreationOptions.None,
taskScheduler);
return reportProgressTask;
}
Run Code Online (Sandbox Code Playgroud)
正如他们所说,安排任务有多种方法。)
SynchronizationContext
是使用虚方法的抽象。使用 SynchronizationContext
允许您不将您的实现绑定到特定框架。
示例:Windows 窗体使用WindowsFormSynchronizationContext
覆盖 Post 的 调用Control.BeginInvoke
. WPF 使用DispatcherSynchronizationContext
覆盖 Post的类型来调用Dispatcher.BeginInvoke
. 您可以设计使用SynchronizationContext
特定框架而不将实现绑定到特定框架的组件。