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();
}
}
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.
Couldn't expect better, an answer from the Guru of async, thanks :)
How about skip the
TaskFactory
and just write this:Task.CompletedTask.ContinueWith( t => DoWork(), scheduler )
?@HappyNomad: The point of
TaskFactory
is that you don't have to repeat all the parameters each time you want to start a task.@StephenCleary The original question asks for
Task.Run
behavior except with a differentscheduler
. I think thatTask.CompletedTask.ContinueWith
is a better choice since not even once do you have to supply those parameters that you're uninterested in thinking about or changing.@StephenCleary I just noticed that
ContinueWith
defaults toTaskContinuationOptions.None
. I guess you'd recommend against this defaultContinueWIth
behavior? That'd leaveTaskFactory
as the best option after all.