首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何获取Dispatcher的TaskScheduler?

如何获取Dispatcher的TaskScheduler?
EN

Stack Overflow用户
提问于 2011-06-16 16:09:31
回答 5查看 7.5K关注 0票数 20

我有一个具有多个Dispatcher的应用程序(又称GUI线程,又称消息泵),以确保GUI的缓慢、无响应部分运行,而不会对应用程序的其余部分造成太大影响。我也经常使用Task

目前,我已经获得了有条件地在TaskSchedulerDispatcher上运行Action的代码,然后直接或通过使用TaskCompletionSource手动创建返回一个Task。然而,这种人格分裂的设计使得处理取消、异常等问题比我想象的要复杂得多。我想在任何地方使用Tasks,而不使用DispatcherOperations。要做到这一点,我需要在dispatchers上调度任务--但是怎么做呢?

如何为任何给定的Dispatcher获取TaskScheduler

编辑:经过下面的讨论,我决定了以下实现:

代码语言:javascript
复制
public static Task<TaskScheduler> GetScheduler(Dispatcher d) {
    var schedulerResult = new TaskCompletionSource<TaskScheduler>();
    d.BeginInvoke(() => 
        schedulerResult.SetResult(
            TaskScheduler.FromCurrentSynchronizationContext()));
    return schedulerResult.Task;
}
EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-05-03 00:07:42

步骤1:创建扩展方法:

代码语言:javascript
复制
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:使用扩展方法:

旧语法

代码语言:javascript
复制
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 (() => { ... });

新语法

代码语言:javascript
复制
var taskScheduler = await Dispatcher.CurrentDispatcher.ToTaskSchedulerAsync ();
var taskFactory = new TaskFactory (taskScheduler);
var task = taskFactory.StartNew (() => { ... });
票数 16
EN

Stack Overflow用户

发布于 2011-06-16 20:22:26

不幸的是,没有内置的方法可以做到这一点。没有专门用于在TaskScheduler中包装Dispatcher的内置类-我们拥有的最接近的是包装SynchronizationContext的类。用于从SynchronizationContext构建TaskScheduler的唯一公共API是Paul Michalik提到的:TaskScheduler.FromCurrentSynchronizationContext -正如您所观察到的,只有当您已经处于相关的同步上下文中(即,在相关的dispatcher的线程中)时,它才能起作用。

所以你有三个选择:

  1. 安排您的代码,以便需要相关调度程序的调度程序的类在某个时刻可以在这些调度程序的线程上运行,这样您就可以按预期使用TaskScheduler.FromCurrentSynchronizationContext
  2. 使用Dispatcher.BeginInvoke在调度程序线程上运行一些代码,并在这些代码中调用TaskScheduler.FromCurrentSynchronizationContext。(换句话说,如果你不能安排1.自然地发生,强制它去happen.)
  3. Write你自己的任务调度程序。
票数 9
EN

Stack Overflow用户

发布于 2011-06-16 16:25:41

看看TaskScheduler.FromCurrentSynchronizationContext吧。Task框架提供了一种非常灵活的方式来配置计算限制操作的执行,即使应用程序强加了特定的线程模型也是如此。

编辑:

嗯,很难从你的帖子中得到更明确的信息。我知道你正在运行一种多视图应用程序,每个视图都有单独的调度程序,对吧?由于所有调度都归结为获取SynchronizationContextPost-ing,因此您可以在视图获得的某个点获取正确的TaskScheduler (具有正确的SynchronizationContext)。要做到这一点,一种简单的方法是在配置taks期间获取一个TaskScheduler:

代码语言:javascript
复制
 // 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();

不确定这是否有帮助,如果你广泛使用任务,你可能已经知道了……

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6368885

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档