我需要运行一些需要使用特定调度程序运行的任务,否则它们将失败,因为它们实例化的对象只能来自特定线程。
使用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();
}
}
你想要的是TaskFactory
。只需创建一个您想要的TaskScheduler
。另外,您通常不希望使用默认选项设置。假设您正在使用与的任务await
,则通常至少需要DenyChildAttach
:
var factory = new TaskFactory(CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.ExecuteSynchronously,
scheduler);
一旦创建(一次),您就可以用于factory.StartNew
排队工作,而不必每次都传递所有参数。
没想到会更好,来自异步大师的答案,谢谢:)
如何跳过
TaskFactory
,只是写:Task.CompletedTask.ContinueWith( t => DoWork(), scheduler )
?@HappyNomad:的要点
TaskFactory
是,您不必在每次要开始任务时都重复所有参数。@StephenCleary最初的问题要求
Task.Run
行为,除非使用不同的scheduler
。我认为这Task.CompletedTask.ContinueWith
是一个更好的选择,因为您甚至不必提供一次您对思考或更改不感兴趣的参数。@StephenCleary我刚刚注意到
ContinueWith
默认为TaskContinuationOptions.None
。我猜您会建议您反对这种默认ContinueWIth
行为?TaskFactory
毕竟,那将是最好的选择。