我有一个具有多个Dispatcher的应用程序(又称GUI线程,又称消息泵),以确保GUI的缓慢、无响应部分运行,而不会对应用程序的其余部分造成太大影响。我也经常使用Task。
目前,我已经获得了有条件地在TaskScheduler或Dispatcher上运行Action的代码,然后直接或通过使用TaskCompletionSource手动创建返回一个Task。然而,这种人格分裂的设计使得处理取消、异常等问题比我想象的要复杂得多。我想在任何地方使用Tasks,而不使用DispatcherOperations。要做到这一点,我需要在dispatchers上调度任务--但是怎么做呢?
如何为任何给定的Dispatcher获取TaskScheduler
编辑:经过下面的讨论,我决定了以下实现:
public static Task<TaskScheduler> GetScheduler(Dispatcher d) {
var schedulerResult = new TaskCompletionSource<TaskScheduler>();
d.BeginInvoke(() =>
schedulerResult.SetResult(
TaskScheduler.FromCurrentSynchronizationContext()));
return schedulerResult.Task;
}发布于 2012-05-03 00:07:42
步骤1:创建扩展方法:
public static Task<TaskScheduler> ToTaskSchedulerAsync (
this Dispatcher dispatcher,
DispatcherPriority priority = DispatcherPriority.Normal) {
var taskCompletionSource = new TaskCompletionSource<TaskScheduler> ();
var invocation = dispatcher.BeginInvoke (new Action (() =>
taskCompletionSource.SetResult (
TaskScheduler.FromCurrentSynchronizationContext ())), priority);
invocation.Aborted += (s, e) =>
taskCompletionSource.SetCanceled ();
return taskCompletionSource.Task;
}步骤2:使用扩展方法:
旧语法
var taskSchedulerAsync = Dispatcher.CurrentDispatcher.ToTaskSchedulerAsync ();
var taskFactoryAsync = taskSchedulerAsync.ContinueWith<TaskFactory> (_ =>
new TaskFactory (taskSchedulerAsync.Result), TaskContinuationOptions.OnlyOnRanToCompletion);
// this is the only blocking statement, not needed once we have await
var taskFactory = taskFactoryAsync.Result;
var task = taskFactory.StartNew (() => { ... });新语法
var taskScheduler = await Dispatcher.CurrentDispatcher.ToTaskSchedulerAsync ();
var taskFactory = new TaskFactory (taskScheduler);
var task = taskFactory.StartNew (() => { ... });发布于 2011-06-16 20:22:26
不幸的是,没有内置的方法可以做到这一点。没有专门用于在TaskScheduler中包装Dispatcher的内置类-我们拥有的最接近的是包装SynchronizationContext的类。用于从SynchronizationContext构建TaskScheduler的唯一公共API是Paul Michalik提到的:TaskScheduler.FromCurrentSynchronizationContext -正如您所观察到的,只有当您已经处于相关的同步上下文中(即,在相关的dispatcher的线程中)时,它才能起作用。
所以你有三个选择:
TaskScheduler.FromCurrentSynchronizationContext。Dispatcher.BeginInvoke在调度程序线程上运行一些代码,并在这些代码中调用TaskScheduler.FromCurrentSynchronizationContext。(换句话说,如果你不能安排1.自然地发生,强制它去happen.)发布于 2011-06-16 16:25:41
看看TaskScheduler.FromCurrentSynchronizationContext吧。Task框架提供了一种非常灵活的方式来配置计算限制操作的执行,即使应用程序强加了特定的线程模型也是如此。
编辑:
嗯,很难从你的帖子中得到更明确的信息。我知道你正在运行一种多视图应用程序,每个视图都有单独的调度程序,对吧?由于所有调度都归结为获取SynchronizationContext和Post-ing,因此您可以在视图获得的某个点获取正确的TaskScheduler (具有正确的SynchronizationContext)。要做到这一点,一种简单的方法是在配置taks期间获取一个TaskScheduler:
// somewhere on GUI thread you wish to invoke
// a long running operation which returns an Int32 and posts
// its result in a control accessible via this.Text
(new Task<Int32>(DoSomeAsyncOperationReturningInt32)
.ContinueWith(tTask => this.Text = tTask.Result.ToString(),
TaskScheduler.FromCurrentSynchronizationContext)).Start();不确定这是否有帮助,如果你广泛使用任务,你可能已经知道了……
https://stackoverflow.com/questions/6368885
复制相似问题