gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 23:07:17 25 4
gpt4 key购买 nike

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

我认为我必须在我的案例中使用 Factory.StartNew(),因为我想插入我自己的 TaskScheduler。

所以总结起来,似乎是:

Task.Run(action)

严格等同于:

Task.Factory.StartNew(action, 
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.Default);

但是,我用一个简单的 SerialQueue 抓取了一些测试 from Microsoft's samples for Parallel Programming with the .NET Framework .

这是简单的代码:

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

现在这是我的一些模拟组合任务的代码(包括等待/继续)。

    public static async Task SimulateTaskSequence(int taskId)
{
Console.WriteLine("Task{0} - Start working 1sec (ManagedThreadId={1} IsThreadPoolThread={2})", taskId, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
Thread.Sleep(200);

Console.WriteLine("Task{0} - Zzz 1st 1sec (ManagedThreadId={1} IsThreadPoolThread={2})", taskId, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
await Task.Delay(200);

Console.WriteLine("Task{0} - Done (ManagedThreadId={1} IsThreadPoolThread={2})", taskId, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
}

测试 1: 将队列与 Task.Run() 结合使用:

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.Run(() => { return SimulateTaskSequence(local); }));
}
}

并且结果是正确的,队列按照预期的顺序处理(在切换到 Task1 之前实现 Task0)。

Starting test program (ManagedThreadId=1 IsThreadPoolThread=False)
Task0 - Start working 1sec (ManagedThreadId=5 IsThreadPoolThread=True)
Task0 - Zzz 1st 1sec (ManagedThreadId=5 IsThreadPoolThread=True)
Task0 - Done (ManagedThreadId=5 IsThreadPoolThread=True)
Task1 - Start working 1sec (ManagedThreadId=5 IsThreadPoolThread=True)
Task1 - Zzz 1st 1sec (ManagedThreadId=5 IsThreadPoolThread=True)
Task1 - Done (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));
}
}

但是这次我得到以下输出:

Starting test program (ManagedThreadId=1 IsThreadPoolThread=False)
Task0 - Start working 1sec (ManagedThreadId=5 IsThreadPoolThread=True)
Task0 - Zzz 1st 1sec (ManagedThreadId=5 IsThreadPoolThread=True)
Task1 - Start working 1sec (ManagedThreadId=5 IsThreadPoolThread=True) WHAT?
Task1 - Zzz 1st 1sec (ManagedThreadId=5 IsThreadPoolThread=True)
Task0 - Done (ManagedThreadId=9 IsThreadPoolThread=True)
Task1 - Done (ManagedThreadId=5 IsThreadPoolThread=True)

我不明白有什么区别。为什么行为不同?我以为是等价的?! (记住,后面的步骤是插入我自己的调度器)

最佳答案

任务工厂的返回类型是Task <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());
}
}

Task.Run 有一个接受 Func<Task> 的重载那是为你做的。如果您在 Task.Run 中将委托(delegate)声明为 Func<object>您会在 Task.Run 中看到相同的行为。

关于c# - Factory.StartNew 和 Task.Run 之间的不同行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47460662/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com