C#任务什么时候实际开始?

42

任务实际开始的时间是什么时候?

public void DoSomething() {
    Task myTask = DoSomethingAsync();

    Task.WaitAll(new[] { myTask }, 2000);
}

public async Task DoSomethingAsync() {
    await SomethingElse();
}

Task myTask = DoSomethingAsync(); 中初始化时,它是否立即启动,还是当您在 Task.WaitAll(new[] { myTask }, 2000); 中等待它时才启动?


3
这是一个很大的问题。首先可以看看这里:http://blog.stephencleary.com/2013/11/there-is-no-thread.html - Matthew Watson
1
Patrick Hofman已经给出了正确的答案。对于你的例子:DoSomethingAsync()将立即运行,尽可能地运行到内部某个尚未完成的任务等待的第一个点。只有在这之后它才会返回并附加继续执行。如果DoSomethingAsync()内部有数十个嵌套的等待函数,其中所有函数都同步完成(例如最后一个是Task.FromResult()),那么在控制权返回给调用者之前,myTask将被完成。 - Matthias247
2个回答

62

调用一个async方法会返回一个热的任务,即已经启动的任务。因此,没有实际的代码需要强制其运行。

根据 MSDN (感谢 Stephen Cleary) 的说法,基于任务的异步模式(TAP)模式要求返回的任务是热的。这意味着除了使用new Task创建的任务之外,所有任务都将是热的。

引用文章中的一段话:

由公共Task构造函数创建的任务称为冷任务... 所有其他任务都以热状态开始其生命周期。


2
当您调用一个不带async的返回Task的方法时,它将不会被启动,这听起来有点令人困惑。大多数返回Task而不带async的方法仍然返回已经启动的Task,因为它们在方法内部启动它。 - Evk
@Evk “自动” > 更好? - Patrick Hofman
3
“async”修饰符由编译器用于构建状态机。除了“Task”/“Task<T>”的构造函数之外,我能想到的任何API都会返回一个热任务。这就是“Task.Run”的作用。那么,“当您调用不带‘async’的返回‘Task’的方法时,它不会自动启动”是什么意思? - Paulo Morgado
返回新的任务... 例如@PauloMorgado - Patrick Hofman

-1
我想提供一些例子,尽管Patrick Hofman已经给出了答案(特别是指向链接文章https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?redirectedfrom=MSDN#task-status中的这一部分)。
希望这些例子能帮助解释。
如果你从简单的HOT任务开始,你会发现它们一旦被初始化就会立即开始。
// Using just some HOT task
var first = Task.Delay(2_500);
var second = Task.Delay(2_500);

await Task.Delay(2_500);

Console.WriteLine("Start awaiting HOT tasks");
var stopwatch = Stopwatch.StartNew();
await Task.WhenAll(first, second);
Console.WriteLine(stopwatch.Elapsed);

以上代码片段将输出以下内容,验证任务在初始化和await Task.WhenAll(…)之间一直在运行。
Start awaiting HOT tasks
00:00:00.0000866

现在,如果您使用了Task构造函数,任务将不会被启动,而是处于"TaskStatus.Created"状态。我们需要显式地启动它们,如下面的示例所示:
// Using Task constructor
var firstNew = new Task(() => Task.Delay(2_500).GetAwaiter().GetResult());
var secondNew = new Task(() => Task.Delay(2_500).GetAwaiter().GetResult());

await Task.Delay(2_500);

Debug.Assert(firstNew.Status == TaskStatus.Created && secondNew.Status == TaskStatus.Created);
firstNew.Start();
secondNew.Start();
Console.WriteLine("Start awaiting NEW tasks");
stopwatch.Restart();
await Task.WhenAll(firstNew, secondNew);
Console.WriteLine(stopwatch.Elapsed);

以上代码输出如下:
Start awaiting NEW tasks
00:00:02.5070535

如果你使用 Task.Run 或者 Task.Factory,它们会返回已启动的任务。举个例子,下面的代码:
// Run
var firstRun = Task.Run(async () => await Task.Delay(2_500));
var secondRun = Task.Run(async () => await Task.Delay(2_500));

await Task.Delay(2_500);

Console.WriteLine("Start awaiting RUN tasks");
stopwatch.Restart();
await Task.WhenAll(firstRun, secondRun);
Console.WriteLine(stopwatch.Elapsed);

// Factory 
var firstFactory = Task.Factory.StartNew(async () => await Task.Delay(2_500));
var secondFactory = Task.Factory.StartNew(async () => await Task.Delay(2_500));

await Task.Delay(2_500);

Console.WriteLine("Start awaiting FACTORY tasks");
stopwatch.Restart();
await Task.WhenAll(firstFactory, secondFactory);
Console.WriteLine(stopwatch.Elapsed);

输出:
Start awaiting RUN tasks
00:00:00.0002207
Start awaiting FACTORY tasks
00:00:00.0005118

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接