如何更改Task.Run的调度程序?

时间:2019-03-27 20:03:15

标签: c# unity3d task

我需要运行一些需要使用特定调度程序运行的任务,否则它们将失败,因为它们实例化的对象只能来自特定线程。

使用Task.Factory.StartNew效果很好,唯一的是语法有点麻烦。

因此,我想到了编写一种扩展方法的想法,该方法可以使我保留Task.Run的简洁语法,但可以指定除TaskScheduler.Default之外的另一个调度程序。但是,我很难弄清楚如何编写这种扩展方法。

问题:

如果可能的话,如何更改要运行的Task.Run的调度程序?

代码示例:

using System;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    private async void Test()
    {
        // game objects can only be created from Unity main thread, get its scheduler

        var scheduler = TaskScheduler.FromCurrentSynchronizationContext();

        // 1. syntax using factory, works but is a bit cumbersome

        await Task.Factory.StartNew(
            () => new GameObject("test"),
            CancellationToken.None,
            TaskCreationOptions.None,
            scheduler
        );

        // 2. ideal syntax though it will fail since it'll run with the wrong scheduler

        await Task.Run(() => new GameObject("test"));

        // 3. ideal syntax but how to implement .Schedule method?

        await Task.Run(() => new GameObject("test")).Schedule(scheduler);
    }
}

扩展方法:

public static class Extensions
{
    public static Task<T> Schedule<T>(this Task<T> task, TaskScheduler scheduler)
    {
        // how, if possible at all to write this method?

        throw new NotImplementedException();
    }
}

2 个答案:

答案 0 :(得分:1)

您根本无法做到这一点。到您从Task返回Task.Run时,该任务已经在默认调度程序上进行了调度,并且可能已经在运行。如果要使用自定义调度程序,请不要使用Task.Run。使用Task.Factory.StartNew,或编写自己的方法以特定方式安排新任务。

答案 1 :(得分:1)

您想要的是TaskFactory。只需使用所需的TaskScheduler创建一个即可。另外,您通常不希望使用默认选项设置。假设您正在使用await的任务,通常通常至少需要DenyChildAttach

var factory = new TaskFactory(CancellationToken.None,
    TaskCreationOptions.DenyChildAttach,
    TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.ExecuteSynchronously,
    scheduler);

创建一次(一次)后,您可以使用factory.StartNew将工作排队,而不必每次都传递所有参数。