wpf 如何获取Dispatcher的TaskScheduler?

vxf3dgd4  于 2023-08-07  发布在  其他
关注(0)|答案(6)|浏览(133)

我有一个带有多个Dispatcher(又名GUI线程,又名消息泵)的应用程序,以确保GUI中运行缓慢、无响应的部分不会严重影响应用程序的其余部分。我也经常使用Task
目前,我有一段代码,它有条件地在TaskSchedulerDispatcher上运行Action,然后直接返回Task,或者使用TaskCompletionSource手动创建一个Task。然而,这种人格分裂的设计使得处理取消,例外等。比我想象的要复杂得多我想在任何地方都使用Task,而在任何地方都不使用DispatcherOperation。要做到这一点,我需要调度任务-但如何?
我如何为任何给定的Dispatcher获取TaskScheduler

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

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

字符串

ttp71kqs

ttp71kqs1#

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

xxe27gdn

xxe27gdn2#

不幸的是,没有内置的方法来做到这一点。没有专门用于将Dispatcher Package 在TaskScheduler中的内置类-我们拥有的最接近的东西是 Package SynchronizationContext的类。从SynchronizationContext构建TaskScheduler的唯一公共API是Paul Michalik提到的:TaskScheduler.FromCurrentSynchronizationContext-正如您所观察到的,只有当您已经在相关的同步上下文中(即,在相关的调度程序的线程上)时,它才有效。
所以你有三个选择
1.安排好代码,使需要调度器的类在某些时候能够在这些调度器的线程上运行,这样就可以按预期使用TaskScheduler.FromCurrentSynchronizationContext
1.使用Dispatcher.BeginInvoke在调度器线程上运行一些代码,并在该代码中调用TaskScheduler.FromCurrentSynchronizationContext。(如果你不能安排1。自然发生,强迫它发生。)
1.编写自己的任务调度程序。

uqjltbpv

uqjltbpv3#

看看TaskScheduler.FromCurrentSynchronizationContext。Task框架提供了一种非常灵活的方式来配置计算绑定操作的执行,即使应用程序强加了特定的线程模型。
编辑:
嗯,很难从你发布的内容中得到更明确的信息。我知道你正在运行一个多视图应用程序,每个视图都有单独的调度程序,对吗?由于所有的调度都归结为获取SynchronizationContextPost-ing,因此您可以在您的视图获得TaskScheduler的某个点获取正确的TaskScheduler(具有正确SynchronizationContextTaskScheduler)。一种简单的方法是在tak配置期间获取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();

字符串
不知道这是否有帮助,如果你正在广泛使用任务,你可能已经知道...

2w2cym1i

2w2cym1i4#

你可以把整个函数写在一行中:

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());

2eafrhcq

2eafrhcq5#

这就是我总是如何将async函数调用转换为sync函数调用的方法(对互联网上的某个人来说值得称赞):

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
    }

j8yoct9x

j8yoct9x6#

虽然TaskScheduler中没有专门用于 Package Dispatcher的内置类,但允许您自己编写一个。执行此任务的示例实现如下所示:

internal class DispatcherTaskScheduler : TaskScheduler
{
    private readonly Dispatcher _dispatcher;
    private readonly SendOrPostCallback _dispatcherCallback;

    public DispatcherTaskScheduler(Dispatcher dispatcher)
    {
        _dispatcher = dispatcher;
        // Callback for dispatcher.
        _dispatcherCallback = (state) =>
        {
            _ = TryExecuteTask((Task)state!);
        };
    }

    public override int MaximumConcurrencyLevel
    {
        // Dispatcher operates on one thread only.
        get => 1;
    }

    protected override IEnumerable<Task> GetScheduledTasks()
    {
        // Whole method is only for debugging purposes. No need to implement it.
        return Array.Empty<Task>();
    }

    protected override void QueueTask(Task task)
    {
        // Schedule the task for execution.
        _ = _dispatcher.BeginInvoke(DispatcherPriority.Normal, _dispatcherCallback, task);
    }

    protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
    {
        // If we are in the right thread then execute the task.
        if (_dispatcher.CheckAccess())
            return TryExecuteTask(task);
        return false;
    }
}

字符串

相关问题