如何获取Dispatcher的TaskScheduler?

时间:2011-06-16 08:09:31

标签: .net wpf multithreading task dispatcher

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

目前我的代码有条件地在ActionTaskScheduler上运行Dispatcher,然后直接或通过使用{手动创建一个Task返回TaskCompletionSource {1}}。然而,这种分裂的个性设计使得处理取消,异常等等都比我想要的复杂得多。我想在任何地方使用TaskDispatcherOperation无处可去。要做到这一点,我需要在调度员上安排任务 - 但是如何?

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

编辑:经过以下讨论,我决定采用以下措施:

public static Task<TaskScheduler> GetScheduler(Dispatcher d) {
    var schedulerResult = new TaskCompletionSource<TaskScheduler>();
    d.BeginInvoke(() => 
        schedulerResult.SetResult(
            TaskScheduler.FromCurrentSynchronizationContext()));
    return schedulerResult.Task;
}

5 个答案:

答案 0 :(得分:14)

第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 (() => { ... });

答案 1 :(得分:9)

不幸的是,没有内置的方法来做到这一点。没有专门用于在Dispatcher中包裹TaskScheduler的内置类 - 我们最接近的是包裹SynchronizationContext的内容。从TaskScheduler构建SynchronizationContext的唯一公开API是Paul Michalik提到的TaskScheduler.FromCurrentSynchronizationContext - 并且正如您所观察到的那样,只有在您已经处于相关同步状态时才有效上下文(即,在相关调度员的主题上)。

所以你有三个选择:

  1. 安排您的代码,以便需要相关调度程序的调度程序的类在某些时候有机会在这些调度程序的线程上运行,以便您可以按预期使用TaskScheduler.FromCurrentSynchronizationContext
  2. 使用Dispatcher.BeginInvoke在调度程序线程上运行一些代码,并在该代码中调用TaskScheduler.FromCurrentSynchronizationContext。 (换句话说,如果你不能安排1.自然发生,强迫它发生。)
  3. 编写自己的任务计划程序。

答案 2 :(得分:4)

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

编辑:

嗯,很难从你发布的内容中得到更明确的信息。我知道你正在运行一种多视图应用程序,每个视图都有独立的调度程序,对吗?由于所有调度归结为提取SynchronizationContextPost - 您可以在某处获取正确的TaskScheduler(具有正确的SynchronizationContext的那个)你的观点有一个。一种简单的方法是在配置过程中获取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();

不确定这是否有帮助,如果您正在广泛使用任务,那么您可能已经知道......

答案 3 :(得分:3)

您可以将整个功能写在一行:

public static Task<TaskScheduler> ToTaskSchedulerAsync(this Dispatcher dispatcher,
                           DispatcherPriority priority = DispatcherPriority.Normal)
{
    return dispatcher.InvokeAsync<TaskScheduler>(() =>
         TaskScheduler.FromCurrentSynchronizationContext(), priority).Task;
}

以及那些对默认UI线程感兴趣的人可能会发现以下内容足以获得:

var ts = Application.Current.Dispatcher.Invoke<TaskScheduler>(() => TaskScheduler.FromCurrentSynchronizationContext());

答案 4 :(得分:0)

这就是我总是将异步函数调用转换为同步函数调用(对互联网上某人的称赞):

    public static class ThreadingUtils 
    {
         public static TaskScheduler GetScheduler(Dispatcher dispatcher)
         {
             using (var waiter = new ManualResetEvent(false))
             {
                 TaskScheduler scheduler = null;
                 dispatcher.BeginInvoke(new Action(() =>
                 {
                     scheduler = 
                         TaskScheduler.FromCurrentSynchronizationContext();
                     waiter.Set();
                 }));
                 waiter.WaitOne();
                 return scheduler;
             }
         }
    }

变异:

    if (!waiter.WaitOne(2000))
    { 
        //Timeout connecting to server, log and exit
    }