Warm tip: This article is reproduced from stackoverflow.com, please click
c# task unity3d

How can the scheduler of Task.Run be changed?

发布于 2020-04-22 11:09:22

I need to run a few tasks that require to be ran using a specific scheduler as they would fail otherwise since the objects they do instantiate can only be from a specific thread.

Using Task.Factory.StartNew works well, the only thing is that the syntax is a bit cumbersome.

So I came up with the idea of writing an extension method that could allow me to keep the terse syntax of Task.Run but specify another scheduler than TaskScheduler.Default. I am having a hard time figuring out how to write such extension method, however.

Question:

How can I change the scheduler of Task.Run it is meant to run with, if possible at all?

Code example:

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

Extension method:

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();
    }
}
Questioner
Aybe
Viewed
42
Stephen Cleary 2019-04-02 03:56

What you want is TaskFactory. Just create one with your desired TaskScheduler. Also, you generally do not want to use the default options settings. Assuming you're using the tasks with await, you generally want at least DenyChildAttach:

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

Once created (one time), you can use factory.StartNew to queue work without having to pass all the parameters every time.