在方法上使用 await Task.Factory.StartNew 会立即 return
Using await Task.Factory.StartNew on a method will return immediately
我是 运行 以下代码(C#7.1 控制台应用程序),我无法真正理解行为差异的原因。
如果我等待常规异步方法调用或 Task.Run - 它会按预期工作(即应用程序不会立即 return)。但是如果我使用 Task.Factory.StartNew - 它会立即 return 而实际上没有代码 运行.
奇怪的是 - 如果我使用 StartNew 但在方法内部删除 await,它不会立即 return...
问题:这 return 立即:
static async Task Main(string[] args)
{
await Task.Factory.StartNew(DisplayCurrentInfo);
}
static async Task DisplayCurrentInfo()
{
await WaitAndApologize();
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Thread.Sleep(3000);
}
即- 我看不到任何打印到控制台的内容,并且控制台已经关闭。
没问题:这不会return立即:
static async Task Main(string[] args)
{
await DisplayCurrentInfo(); // or await Task.Run(DisplayCurrentInfo);
}
static async Task DisplayCurrentInfo()
{
await WaitAndApologize();
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Thread.Sleep(3000);
}
奇怪:这也不return立即:
static async Task Main(string[] args)
{
await Task.Factory.StartNew(DisplayCurrentInfo);
}
static async Task DisplayCurrentInfo()
{
WaitAndApologize();
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Thread.Sleep(3000);
}
等待并道歉:
static async Task WaitAndApologize()
{
// Task.Delay is a placeholder for actual work.
await Task.Delay(2000);
// Task.Delay delays the following line by two seconds.
Console.WriteLine("\nSorry for the delay. . . .\n");
}
如果您使用 Task.Factory.StartNew(MethodThatReturnsTask)
,您会返回 Task<Task<T>>
或 Task<Task>
,具体取决于该方法是否返回通用任务。
最终结果是你有2个任务:
Task.Factory.StartNew
产生一个调用 MethodThatReturnsTask
的任务,我们称这个任务为 "Task A"
MethodThatReturnsTask
在你的例子中 returns a Task
,我们称它为 "Task B",这意味着使用了处理此问题的 StartNew
的重载最终的结果是你得到了一个包装了任务 B 的任务 A。
要"correctly"等待这些任务需要2个等待,而不是1个。您的单个等待只是等待任务A,这意味着当它returns时,任务B仍在执行等待完成。
要天真地回答您的问题,请使用 2 个等待:
await await Task.Factory.StartNew(DisplayCurrentInfo);
但是,值得怀疑的是,为什么您需要生成一个任务来启动另一个异步方法。相反,你最好使用第二种语法,你只需等待方法:
await DisplayCurrentInfo();
意见如下:一般来说,一旦您开始编写异步代码,就应该保留使用 Task.Factory.StartNew
或其任何兄弟方法,以备不时之需产生一个 thread (或类似的东西)来调用不与其他东西并行异步的东西。如果您不需要此特定模式,最好不要使用它。
当您调用 Task.Factory.StartNew(DisplayCurrentInfo);
时,您使用的是签名:
public Task<TResult> StartNew<TResult>(Func<TResult> function)
现在,由于您正在传递一个带有签名 Task DisplayCurrentInfo()
的方法,因此 TResult
类型是 Task
。
换句话说,您正在从 Task.Factory.StartNew(DisplayCurrentInfo)
返回一个 Task<Task>
,然后您正在等待 returns Task
的外部任务 - 而您并没有在等待那个.因此它立即完成。
await Task.Factory.StartNew(DisplayCurrentInfo);
第一个示例立即执行 return,因为您正在创建一个新任务并等待它,这将调用另一个任务,但不会等待。考虑如下所示的 伪代码,以便继续等待。
await Task.Factory.StartNew( await DisplayCurrentInfo); //Imaginary code to understand
在第三个例子中 WaitAndApologize
不是 awaited,而是被线程睡眠阻塞(全线程被阻塞)。
仅在代码的帮助下,我们不能说任务工厂已经创建了一个新线程或只是 运行 在现有线程上。
如果它的 运行 在同一个线程中,整个线程都会被阻塞,所以你会感觉到代码正在等待 await Task.Factory.StartNew(DisplayCurrentInfo);
,但实际上它没有发生。
如果它 运行 在不同的线程上,您将不会得到与上面相同的结果。
我是 运行 以下代码(C#7.1 控制台应用程序),我无法真正理解行为差异的原因。
如果我等待常规异步方法调用或 Task.Run - 它会按预期工作(即应用程序不会立即 return)。但是如果我使用 Task.Factory.StartNew - 它会立即 return 而实际上没有代码 运行.
奇怪的是 - 如果我使用 StartNew 但在方法内部删除 await,它不会立即 return...
问题:这 return 立即:
static async Task Main(string[] args)
{
await Task.Factory.StartNew(DisplayCurrentInfo);
}
static async Task DisplayCurrentInfo()
{
await WaitAndApologize();
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Thread.Sleep(3000);
}
即- 我看不到任何打印到控制台的内容,并且控制台已经关闭。
没问题:这不会return立即:
static async Task Main(string[] args)
{
await DisplayCurrentInfo(); // or await Task.Run(DisplayCurrentInfo);
}
static async Task DisplayCurrentInfo()
{
await WaitAndApologize();
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Thread.Sleep(3000);
}
奇怪:这也不return立即:
static async Task Main(string[] args)
{
await Task.Factory.StartNew(DisplayCurrentInfo);
}
static async Task DisplayCurrentInfo()
{
WaitAndApologize();
Console.WriteLine($"The current time is {DateTime.Now.TimeOfDay:t}");
Thread.Sleep(3000);
}
等待并道歉:
static async Task WaitAndApologize()
{
// Task.Delay is a placeholder for actual work.
await Task.Delay(2000);
// Task.Delay delays the following line by two seconds.
Console.WriteLine("\nSorry for the delay. . . .\n");
}
如果您使用 Task.Factory.StartNew(MethodThatReturnsTask)
,您会返回 Task<Task<T>>
或 Task<Task>
,具体取决于该方法是否返回通用任务。
最终结果是你有2个任务:
Task.Factory.StartNew
产生一个调用MethodThatReturnsTask
的任务,我们称这个任务为 "Task A"MethodThatReturnsTask
在你的例子中 returns aTask
,我们称它为 "Task B",这意味着使用了处理此问题的StartNew
的重载最终的结果是你得到了一个包装了任务 B 的任务 A。
要"correctly"等待这些任务需要2个等待,而不是1个。您的单个等待只是等待任务A,这意味着当它returns时,任务B仍在执行等待完成。
要天真地回答您的问题,请使用 2 个等待:
await await Task.Factory.StartNew(DisplayCurrentInfo);
但是,值得怀疑的是,为什么您需要生成一个任务来启动另一个异步方法。相反,你最好使用第二种语法,你只需等待方法:
await DisplayCurrentInfo();
意见如下:一般来说,一旦您开始编写异步代码,就应该保留使用 Task.Factory.StartNew
或其任何兄弟方法,以备不时之需产生一个 thread (或类似的东西)来调用不与其他东西并行异步的东西。如果您不需要此特定模式,最好不要使用它。
当您调用 Task.Factory.StartNew(DisplayCurrentInfo);
时,您使用的是签名:
public Task<TResult> StartNew<TResult>(Func<TResult> function)
现在,由于您正在传递一个带有签名 Task DisplayCurrentInfo()
的方法,因此 TResult
类型是 Task
。
换句话说,您正在从 Task.Factory.StartNew(DisplayCurrentInfo)
返回一个 Task<Task>
,然后您正在等待 returns Task
的外部任务 - 而您并没有在等待那个.因此它立即完成。
await Task.Factory.StartNew(DisplayCurrentInfo);
第一个示例立即执行 return,因为您正在创建一个新任务并等待它,这将调用另一个任务,但不会等待。考虑如下所示的 伪代码,以便继续等待。
await Task.Factory.StartNew( await DisplayCurrentInfo); //Imaginary code to understand
在第三个例子中 WaitAndApologize
不是 awaited,而是被线程睡眠阻塞(全线程被阻塞)。
仅在代码的帮助下,我们不能说任务工厂已经创建了一个新线程或只是 运行 在现有线程上。
如果它的 运行 在同一个线程中,整个线程都会被阻塞,所以你会感觉到代码正在等待 await Task.Factory.StartNew(DisplayCurrentInfo);
,但实际上它没有发生。
如果它 运行 在不同的线程上,您将不会得到与上面相同的结果。