如果尚未 运行,Task.Wait 是否不启动任务?
Does or does Task.Wait not start the task if it isn't already running?
根据我阅读的 Microsoft TPL 文档 (link),调用 Task.Wait()
方法将阻塞当前线程,直到该任务完成(或取消,或出错)。但它也说如果有问题的任务还没有开始,Wait
方法将尝试 运行 它 在它自己的线程 上询问调度程序重新分配它,从而减少由于阻塞造成的浪费。
我有一个系统,其中任务(一次 运行ning)通过启动其他任务并等待它们的结果来收集数据。这些其他任务依次从其他任务等开始收集数据,可能有几百层深。我真的不想让无数任务阻塞并等待最后的一个任务最终完成。
然而,当我在测试控制台应用程序中尝试这个时,Task.Wait()
似乎根本没有启动任何东西。
构建必须相互等待且浪费周期最少的任务序列的正确咒语是什么?它有点像 ContinueWith,除了从系列中的最后一个任务开始...
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var source = new CancellationTokenSource();
var token = source.Token;
// Create a non-running task.
var task = new Task<string[]>(() => InternalCompute(token), token);
// Isn't this supposed to start the task?
task.Wait(CancellationToken.None);
// I realise this code now won't run until the task finishes,
// it's here for when I use task.Start() instead of task.Wait().
Console.WriteLine("Press any key to cancel the process.");
Console.ReadKey(true);
source.Cancel();
Console.WriteLine("Source cancelled...");
Console.WriteLine("Press any key to quit.");
Console.ReadKey(true);
}
private static string[] InternalCompute(CancellationToken token)
{
string[] data;
try
{
data = Compute(token);
}
catch (TaskCanceledException ex)
{
return null;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return new[] { ex.Message };
}
Console.WriteLine("Post-processor starting.");
for (int i = 0; i < data.Length; i++)
if (data[i] is null)
Console.WriteLine($"Null data at {i}.");
else
Console.WriteLine($"Valid data at {i}.");
Console.WriteLine("Post-processor completed.");
return data;
}
/// <summary>
/// This method stands in for an abstract one to be implemented by plug-in developers.
/// </summary>
private static string[] Compute(CancellationToken token)
{
var data = new string[10];
for (int i = 0; i < data.Length; i++)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(250);
data[i] = i.ToString();
Console.WriteLine($"Computing item {i + 1}...");
}
return data;
}
}
}
Task
s 通常分为两组 - "cold" 任务和 "hot" 任务。 "cold" 任务是尚未开始的任务, 尚未 运行 。 "hot" 任务是当前可能会或可能不会 运行ning 的任务,但重要的是,如果它们尚未 运行ning,则它们可以随时这样做。他们意味着 运行,但尚未分配他们需要的资源(线程)。
this post 所说的是执行一个 "hot" 任务,否则没有机会 运行。 "hot" 任务是通过调用创建的,例如Task.Run()
。他们也是例如您将从异步方法收到的 Task
s 的类型。 new Task(...)
,另一方面给你 "cold" 任务。除非或直到您对该任务调用 Start
或道德等效方法,否则它将保持 "cold"。它显式调用其中一种方法,使其成为 "hot" 而不是 "cold".
通常,您不想一直处理 "cold" 任务,这就是为什么不赞成直接调用 Task
构造函数的原因。在他们弄清楚调度应该如何真正起作用之前,他们确实是一个糟糕的实验。大多数现代代码根本不希望与 "cold" 任务一起工作。
以上post中的关键引用是:
However, if it hasn’t started executing, Wait may be able to pull the target task out of the scheduler to which it was queued and execute it inline on the current thread.
如果您没有在任务上调用 Start
,则它还没有与调度程序一起排队 - 所以显然我们不能执行上述操作。
这是 the article 中引起混淆的部分(强调已添加)。
If the Task being Wait’d on has already started execution, Wait has to block. However, if it hasn’t started executing, Wait may be able to pull the target task out of the scheduler to which it was queued and execute it inline on the current thread.
这是Task.Start
方法的描述:
Starts the Task
, scheduling it for execution to the current TaskScheduler
.
这些是任务生命周期的 eight different stages:
public enum TaskStatus
{
Created = 0, // The task has been initialized but has not yet been scheduled.
WaitingForActivation = 1, // The task is waiting to be activated and scheduled internally by the .NET Framework infrastructure.
WaitingToRun = 2, // The task has been scheduled for execution but has not yet begun executing.
Running = 3, // The task is running but has not yet completed.
WaitingForChildrenToComplete = 4, // The task has finished executing and is implicitly waiting for attached child tasks to complete.
RanToCompletion = 5, // The task completed execution successfully.
Canceled = 6, // The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken while the token was in signaled state, or the task's CancellationToken was already signaled before the task started executing.
Faulted = 7 // The task completed due to an unhandled exception.
}
这篇文章隐含地讨论了处于 WaitingForActivation
或 WaitingToRun
阶段的 hot 任务,并解释了在哪些条件下它们可以当他们的 Wait
方法被调用时,在内部进入 Running
阶段。这不是在谈论 Created
阶段的 cold 任务。如果不调用其 Start
或 RunSynchronously
方法,此阶段的任务将无法进行。换句话说,.NET 基础结构永远不会自动将任务的温度从冷变热。此责任 100% 属于使用 Task
constructor.
创建任务的应用程序代码
作为旁注,值得从评论中引用这句话:
This constructor should only be used in advanced scenarios where it is required that the creation and starting of the task is separated.
根据我阅读的 Microsoft TPL 文档 (link),调用 Task.Wait()
方法将阻塞当前线程,直到该任务完成(或取消,或出错)。但它也说如果有问题的任务还没有开始,Wait
方法将尝试 运行 它 在它自己的线程 上询问调度程序重新分配它,从而减少由于阻塞造成的浪费。
我有一个系统,其中任务(一次 运行ning)通过启动其他任务并等待它们的结果来收集数据。这些其他任务依次从其他任务等开始收集数据,可能有几百层深。我真的不想让无数任务阻塞并等待最后的一个任务最终完成。
然而,当我在测试控制台应用程序中尝试这个时,Task.Wait()
似乎根本没有启动任何东西。
构建必须相互等待且浪费周期最少的任务序列的正确咒语是什么?它有点像 ContinueWith,除了从系列中的最后一个任务开始...
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var source = new CancellationTokenSource();
var token = source.Token;
// Create a non-running task.
var task = new Task<string[]>(() => InternalCompute(token), token);
// Isn't this supposed to start the task?
task.Wait(CancellationToken.None);
// I realise this code now won't run until the task finishes,
// it's here for when I use task.Start() instead of task.Wait().
Console.WriteLine("Press any key to cancel the process.");
Console.ReadKey(true);
source.Cancel();
Console.WriteLine("Source cancelled...");
Console.WriteLine("Press any key to quit.");
Console.ReadKey(true);
}
private static string[] InternalCompute(CancellationToken token)
{
string[] data;
try
{
data = Compute(token);
}
catch (TaskCanceledException ex)
{
return null;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return new[] { ex.Message };
}
Console.WriteLine("Post-processor starting.");
for (int i = 0; i < data.Length; i++)
if (data[i] is null)
Console.WriteLine($"Null data at {i}.");
else
Console.WriteLine($"Valid data at {i}.");
Console.WriteLine("Post-processor completed.");
return data;
}
/// <summary>
/// This method stands in for an abstract one to be implemented by plug-in developers.
/// </summary>
private static string[] Compute(CancellationToken token)
{
var data = new string[10];
for (int i = 0; i < data.Length; i++)
{
token.ThrowIfCancellationRequested();
Thread.Sleep(250);
data[i] = i.ToString();
Console.WriteLine($"Computing item {i + 1}...");
}
return data;
}
}
}
Task
s 通常分为两组 - "cold" 任务和 "hot" 任务。 "cold" 任务是尚未开始的任务, 尚未 运行 。 "hot" 任务是当前可能会或可能不会 运行ning 的任务,但重要的是,如果它们尚未 运行ning,则它们可以随时这样做。他们意味着 运行,但尚未分配他们需要的资源(线程)。
this post 所说的是执行一个 "hot" 任务,否则没有机会 运行。 "hot" 任务是通过调用创建的,例如Task.Run()
。他们也是例如您将从异步方法收到的 Task
s 的类型。 new Task(...)
,另一方面给你 "cold" 任务。除非或直到您对该任务调用 Start
或道德等效方法,否则它将保持 "cold"。它显式调用其中一种方法,使其成为 "hot" 而不是 "cold".
通常,您不想一直处理 "cold" 任务,这就是为什么不赞成直接调用 Task
构造函数的原因。在他们弄清楚调度应该如何真正起作用之前,他们确实是一个糟糕的实验。大多数现代代码根本不希望与 "cold" 任务一起工作。
以上post中的关键引用是:
However, if it hasn’t started executing, Wait may be able to pull the target task out of the scheduler to which it was queued and execute it inline on the current thread.
如果您没有在任务上调用 Start
,则它还没有与调度程序一起排队 - 所以显然我们不能执行上述操作。
这是 the article 中引起混淆的部分(强调已添加)。
If the Task being Wait’d on has already started execution, Wait has to block. However, if it hasn’t started executing, Wait may be able to pull the target task out of the scheduler to which it was queued and execute it inline on the current thread.
这是Task.Start
方法的描述:
Starts the
Task
, scheduling it for execution to the currentTaskScheduler
.
这些是任务生命周期的 eight different stages:
public enum TaskStatus
{
Created = 0, // The task has been initialized but has not yet been scheduled.
WaitingForActivation = 1, // The task is waiting to be activated and scheduled internally by the .NET Framework infrastructure.
WaitingToRun = 2, // The task has been scheduled for execution but has not yet begun executing.
Running = 3, // The task is running but has not yet completed.
WaitingForChildrenToComplete = 4, // The task has finished executing and is implicitly waiting for attached child tasks to complete.
RanToCompletion = 5, // The task completed execution successfully.
Canceled = 6, // The task acknowledged cancellation by throwing an OperationCanceledException with its own CancellationToken while the token was in signaled state, or the task's CancellationToken was already signaled before the task started executing.
Faulted = 7 // The task completed due to an unhandled exception.
}
这篇文章隐含地讨论了处于 WaitingForActivation
或 WaitingToRun
阶段的 hot 任务,并解释了在哪些条件下它们可以当他们的 Wait
方法被调用时,在内部进入 Running
阶段。这不是在谈论 Created
阶段的 cold 任务。如果不调用其 Start
或 RunSynchronously
方法,此阶段的任务将无法进行。换句话说,.NET 基础结构永远不会自动将任务的温度从冷变热。此责任 100% 属于使用 Task
constructor.
作为旁注,值得从评论中引用这句话:
This constructor should only be used in advanced scenarios where it is required that the creation and starting of the task is separated.