C# Factory.StartNew和Task.Run之间的行为不同?

C# Factory.StartNew和Task.Run之间的行为不同?,c#,.net,task,taskscheduler,C#,.net,Task,Taskscheduler,我试图理解Factory.StartNew和Task.Run之间的区别。我在不同的地方看到了等价性,比如 我想我必须使用Factory.StartNew(),因为我想插入自己的TaskScheduler 总之,似乎: Task.Run(action) 严格等同于: Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskSchedu

我试图理解Factory.StartNew和Task.Run之间的区别。我在不同的地方看到了等价性,比如

我想我必须使用Factory.StartNew(),因为我想插入自己的TaskScheduler

总之,似乎:

Task.Run(action)
严格等同于:

Task.Factory.StartNew(action, 
    CancellationToken.None, 
    TaskCreationOptions.DenyChildAttach, 
    TaskScheduler.Default);
但是,我用一个简单的串行队列运行了一些测试

下面是简单的代码:

/// <summary>Represents a queue of tasks to be started and executed serially.</summary>
public class SerialTaskQueue
{
    /// <summary>The ordered queue of tasks to be executed. Also serves as a lock protecting all shared state.</summary>
    private Queue<object> _tasks = new Queue<object>();
    /// <summary>The task currently executing, or null if there is none.</summary>
    private Task _taskInFlight;

    /// <summary>Enqueues the task to be processed serially and in order.</summary>
    /// <param name="taskGenerator">The function that generates a non-started task.</param>
    public void Enqueue(Func<Task> taskGenerator) { EnqueueInternal(taskGenerator); }

    /// <summary>Enqueues the task to be processed serially and in order.</summary>
    /// <param name="taskOrFunction">The task or functino that generates a task.</param>
    /// <remarks>The task must not be started and must only be started by this instance.</remarks>
    private void EnqueueInternal(object taskOrFunction)
    {
        // Validate the task
        if (taskOrFunction == null) throw new ArgumentNullException("task");
        lock (_tasks)
        {
            // If there is currently no task in flight, we'll start this one
            if (_taskInFlight == null) StartTask_CallUnderLock(taskOrFunction);
            // Otherwise, just queue the task to be started later
            else _tasks.Enqueue(taskOrFunction);
        }
    }

    /// <summary>Starts the provided task (or function that returns a task).</summary>
    /// <param name="nextItem">The next task or function that returns a task.</param>
    private void StartTask_CallUnderLock(object nextItem)
    {
        Task next = nextItem as Task;
        if (next == null) next = ((Func<Task>)nextItem)();

        if (next.Status == TaskStatus.Created) next.Start();

        _taskInFlight = next;
        next.ContinueWith(OnTaskCompletion);
    }


    /// <summary>Called when a Task completes to potentially start the next in the queue.</summary>
    /// <param name="ignored">The task that completed.</param>
    private void OnTaskCompletion(Task ignored)
    {
        lock (_tasks)
        {
            // The task completed, so nothing is currently in flight.
            // If there are any tasks in the queue, start the next one.
            _taskInFlight = null;
            if (_tasks.Count > 0) StartTask_CallUnderLock(_tasks.Dequeue());
        }
    }
}
Test1:使用带有Task.Run()的队列:

static void Main(字符串[]args)
{
Console.WriteLine($“正在启动测试程序(ManagedThreadId={Thread.CurrentThread.ManagedThreadId}IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread}”);
SerialTaskQueue co_pQueue=新的SerialTaskQueue();
对于(int i=0;i<2;i++)
{
var局部=i;
co_pQueue.Enqueue(()=>Task.Run(()=>{return SimulateTaskSequence(local);}));
}
}
结果正确,队列按预期顺序处理(在切换到Task1之前实现Task0)

正在启动测试程序(ManagedThreadId=1 IsThreadPoolThread=False)
Task0-开始工作1秒(ManagedThreadId=5 IsThreadPoolThread=True)
Task0-Zzz第1秒(ManagedThreadId=5 IsThreadPoolThread=True)
Task0-完成(ManagedThreadId=5 IsThreadPoolThread=True)
Task1-开始工作1秒(ManagedThreadId=5 IsThreadPoolThread=True)
Task1-Zzz 1st 1sec(ManagedThreadId=5 IsThreadPoolThread=True)
Task1-完成(ManagedThreadId=8 IsThreadPoolThread=True)

测试2:仅使用Factory.StartNew及其完全等效项:

static void Main(string[] args)
{
    Console.WriteLine($"Starting test program (ManagedThreadId={Thread.CurrentThread.ManagedThreadId} IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread})");

    SerialTaskQueue co_pQueue = new SerialTaskQueue();

    for (int i = 0; i < 2; i++)
    {
        var local = i;
        co_pQueue.Enqueue(() => Task.Factory.StartNew(() => { return SimulateTaskSequence(local); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
    }
}
static void Main(字符串[]args)
{
Console.WriteLine($“正在启动测试程序(ManagedThreadId={Thread.CurrentThread.ManagedThreadId}IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread}”);
SerialTaskQueue co_pQueue=新的SerialTaskQueue();
对于(int i=0;i<2;i++)
{
var局部=i;
co_pQueue.Enqueue(()=>Task.Factory.StartNew(()=>{return SimulateTaskSequence(local);},CancellationToken.None,TaskCreationOptions.denychildatach,TaskScheduler.Default));
}
}
但这次我得到了以下输出:

正在启动测试程序(ManagedThreadId=1 IsThreadPoolThread=False)
Task0-开始工作1秒(ManagedThreadId=5 IsThreadPoolThread=True)
Task0-Zzz第1秒(ManagedThreadId=5 IsThreadPoolThread=True)
任务1-开始工作1秒(ManagedThreadId=5 IsThreadPoolThread=True)什么?
Task1-Zzz 1st 1sec(ManagedThreadId=5 IsThreadPoolThread=True)
Task0-完成(ManagedThreadId=9 IsThreadPoolThread=True)
Task1-完成(ManagedThreadId=5 IsThreadPoolThread=True)


我不明白有什么不同。为什么行为不同?我以为这是同等的?!(记住,后面的步骤是插入我自己的计划程序)

任务工厂的返回类型是
task
,task.Run的返回类型就是
task

您需要使用工厂展开内部任务,以便队列代码中的
ConinueWith
在内部任务而不是外部任务上运行延续

static void Main(string[] args)
{
    Console.WriteLine($"Starting test program (ManagedThreadId={Thread.CurrentThread.ManagedThreadId} IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread})");

    SerialTaskQueue co_pQueue = new SerialTaskQueue();

    for (int i = 0; i < 2; i++)
    {
        var local = i;
        co_pQueue.Enqueue(() => Task.Factory.StartNew(() => { return SimulateTaskSequence(local); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap());
    }
}
static void Main(字符串[]args)
{
Console.WriteLine($“正在启动测试程序(ManagedThreadId={Thread.CurrentThread.ManagedThreadId}IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread}”);
SerialTaskQueue co_pQueue=新的SerialTaskQueue();
对于(int i=0;i<2;i++)
{
var局部=i;
co_pQueue.Enqueue(()=>Task.Factory.StartNew(()=>{return SimulateTaskSequence(local);},CancellationToken.None,TaskCreationOptions.denychildatach,TaskScheduler.Default).Unwrap();
}
}

Task.Run有一个重载,它接受为您执行此操作的
Func
。如果您在Task.Run中声明了委托。作为
Func
运行,您将在Task.Run中看到相同的行为。

BTW:StartNew不支持Func by design有什么原因吗?@Christophe,因为只有在.net中添加了async/await并且StartNew比它更旧的情况下才需要展开任务的功能。
static void Main(string[] args)
{
    Console.WriteLine($"Starting test program (ManagedThreadId={Thread.CurrentThread.ManagedThreadId} IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread})");

    SerialTaskQueue co_pQueue = new SerialTaskQueue();

    for (int i = 0; i < 2; i++)
    {
        var local = i;
        co_pQueue.Enqueue(() => Task.Factory.StartNew(() => { return SimulateTaskSequence(local); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default));
    }
}
static void Main(string[] args)
{
    Console.WriteLine($"Starting test program (ManagedThreadId={Thread.CurrentThread.ManagedThreadId} IsThreadPoolThread={Thread.CurrentThread.IsThreadPoolThread})");

    SerialTaskQueue co_pQueue = new SerialTaskQueue();

    for (int i = 0; i < 2; i++)
    {
        var local = i;
        co_pQueue.Enqueue(() => Task.Factory.StartNew(() => { return SimulateTaskSequence(local); }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap());
    }
}