根据Microsoft文档,我读取(链接)调用Task.Wait()方法将阻塞当前线程,直到该任务完成(或取消或错误)。但它还表示,如果问题尚未开始,Wait方法将尝试在自己的线程上运行它,方法是要求调度程序重新分配它,从而减少由于阻塞而造成的浪费。
我有一个系统,在这个系统中,任务(一旦运行)开始收集数据,启动其他任务并等待它们的结果。这些其他任务的开始是从其他任务和潜在数百层深的堡垒收集数据。我真的不想让无数的任务阻塞和等待最后一项任务的最终完成。
然而,当我在一个测试控制台应用程序中尝试这一点时,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;
}
}
}发布于 2018-11-05 15:00:11
Task通常分为两类--“冷”任务和“热”任务。“冷”任务是尚未启动且尚未运行的任务。“热”任务是当前可能正在运行的任务,也可能不是当前正在运行的任务,但重要的是,如果它们尚未运行,它们可能随时都会运行。它们本来是要运行的,但是还没有被分配给它们所需要的资源(线程)。
这个职位正在谈论的是执行一个“热”任务,而这个任务本来还没有机会运行。“热”任务是通过调用例如Task.Run()创建的。它们也是从异步方法获得的Task的类型。另一方面,new Task(...)给你“冷酷”的任务。除非或者直到你在这个任务上调用Start或道义上的等价物,否则它仍然是“冷的”。它明确地将其中一种方法称为“热”而不是“冷”。
通常,这些日子里您都不想处理“冷”任务,这就是为什么直接调用Task构造函数被反对的原因。他们是一个糟糕的实验,从他们之前,他们确定如何安排真正的工作。大多数现代代码根本不希望处理“冷”任务。
以上文章的主要引语是:
但是,如果尚未开始执行,等待可能会将目标任务从其排队的调度程序中提取出来,并在当前线程上内联执行。
如果您没有在任务上调用Start,那么它就没有与调度程序一起排队--所以很明显,我们不能按上面所说的去做。
发布于 2020-04-29 08:27:40
这是文章中引起混淆的部分(着重号是添加的)。
如果正在等待的任务已经开始执行,则等待必须阻塞。但是,如果尚未开始执行,等待可能会将目标任务从其排队的调度程序中提取出来,并在当前线程上内联执行。
这是对Task.Start方法的描述:
启动
Task,将其调度到当前的TaskScheduler上执行。
这些是任务生命周期的八个不同阶段:
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阶段,并解释了在调用其Wait方法时,在哪些条件下可以将其内部进展到Running阶段。它不是在谈论冷任务在Created阶段。如果不调用任务的Start或RunSynchronously方法,则此阶段的任务将无法进行。换句话说,.NET基础结构从不自动将任务的温度从冷更改为热。此责任100%属于使用Task 构造函数创建任务的应用程序代码。
作为附带说明,这句话值得引用如下:
此构造函数只应在需要将任务的创建和启动分开的高级场景中使用。
https://stackoverflow.com/questions/53156253
复制相似问题