我的目标是在“ Task1”之后启动“ Task2”。起初,我在下面编写了类似“ Code1”的代码,但是没有用(Task2在Task1完成之前就启动了)。因此,我搜索了Stackoverflow,并按照建议the existing answer修改了下面的代码,例如“ Code2”。我想知道为什么“ Code1”不起作用。
代码1
static void Main(string[] args)
{
var p = new Program();
p.Test2();
Console.ReadKey();
}
void Test2()
{
Task.Factory.StartNew(async () =>
{
await Task1();
}).ContinueWith((t) => {
Task2();
});
}
async Task Task1()
{
Debug.WriteLine("Task 1 starting....");
await LongTask();
Debug.WriteLine("Task 1 done");
}
Task LongTask()
{
return Task.Factory.StartNew(() =>
{
Thread.Sleep(3000);
});
}
void Task2()
{
Debug.WriteLine("Task 2");
}
代码2
Task.Factory.StartNew(async () =>
{
await Task1();
Task2();
}).ContinueWith((t) => {
//Task2();
});
答案 0 :(得分:2)
因为当您运行Task.Factory.StartNew(async () => ...
之类的任务时,它将返回Task<Task>
(任务的任务)。并且第一个任务终止而无需等待内部Task
。
为防止这种情况,您可以使用Unwrap
方法:
Task.Factory.StartNew(async () =>
{
await Task1();
})
.Unwrap()
.ContinueWith((t) => {
Task2();
});
该StartNew
将返回Task<Task>
,看来您想在内部任务完成后执行继续。这就是为什么我们需要Unwrap
。
它将外部任务返回的内部任务“解包”。在任务上调用Unwrap会给您带来一个新任务(我们通常将其称为代理),它代表内部任务的最终完成。然后,我们在内部任务中添加了延续性。
但是,由于Task.Run
会自动进行解包,因此您可以在这种情况下使用Task.Run
:
Task.Run(async () =>
{
await Task1();
})
.ContinueWith((t) => {
Task2();
});
这可以简化为:
Task.Run(async () =>
{
await Task1();
Task2();
});
有关Task.Run
和Task.Factory.StartNew
之间差异的详细信息:
从.Net团队的工程师Stephen Toub中了解更多here。他们决定在Unwrap
的情况下添加Task.Run
:
因为我们希望这种情况对于人们来说很普遍,所以他们希望卸载工作 到ThreadPool,并为了使用
async/await
进行这项工作,我们决定 将这种展开功能嵌入Task.Run
中。
顺便说一句,正如Stephen所建议的,在大多数情况下,请尝试使用Task.Run
,但这绝不会淘汰Task.Factory.StartNew
,仍然有一些地方必须使用Task.Factory.StartNew
:
Task.Factory.StartNew
仍然有许多重要的功能(尽管更高级) 用途。您可以控制TaskCreationOptions
的任务方式 表现良好。您可以控制任务应在何处进行调度 排队并运行。您可以使用接受对象的重载 状态,对于性能敏感的代码路径可以用来避免 关闭和相应的分配。对于简单的情况, 不过Task.Run
是你的朋友。
答案 1 :(得分:1)
String
不理解异步lambda。对于 String test = "test2";
System.out.println(test.endsWith("[0-9]")); //false
System.out.println(test.matches(".*[0-9]$")); //true
,您的lambda只是函数返回Task.Factory.StartNew
。它不会自动等待该任务。另外,请注意,由于现代C#难以正确使用,因此不建议使用Task.Factory.StartNew
和Task
(请阅读Stephen Cleary的“ StartNew是危险的”)。只有在没有它们的情况下,才应使用Task.Factory.StarNew
或ContinueWith
。
相反,您可以再使用async / await一次:
Task.Factory.StarNew
或ContinueWith
:
async Task Test2()
{
await Task1();
Task2();
}
如果要确保在后台线程上启动异步Task.Run
,则第二种方法可能会很方便。