不了解 C# 中任务的行为
Don't understand behavior of Tasks in C#
我希望有人能帮助我理解一些看似简单的代码。显然,我错过了一些东西。关于这段代码的行为,有两点我不明白:
如果我让代码 运行,'End Delay' 的最后一个 Debug.WriteLine
永远不会被写入。这是为什么?
如果我在 Main() 的 Task.WaitAll()
行上放置一个断点,我看到 delayTask
的状态为 WaitingToRun
并且 'Begin Delay' Debug.WriteLine
in DoDelay()
不会发生,无论我等多久。但是,一旦我在 Task.WaitAll() 行上按 F10,我就会看到 'Begin Delay' 出现在输出中。为什么 Task.WaitAll()
行上的断点似乎甚至会阻止 开始 任务?无论我使用 Task.WaitAll(delayTask)
还是 await delayTask
.
,此行为都不会改变
我运行正在使用 .NET Framework 4.6.2,不幸的是,我别无选择。
感谢发帖的每一个人。我有上面第一个问题的答案 - 我将 'async void DoDelay()' 更改为 'async Task DoDelay()'。但是我仍然对第二个问题中的行为没有任何解释。
更新:正如 JonasH 向我指出的那样,在 VS 中处于中断模式时所有任务都会停止。
对于任何感兴趣的人,这篇文章对理解正在发生的事情有很大帮助:https://www.pluralsight.com/guides/understand-control-flow-async-await
static void Main(string[] args)
{
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(() => DoDelay(10000));
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
Task.WaitAll(delayTask);
}
static async void DoDelay(int delayMs)
{
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
一些注意事项:避免使用 async void 因为你必须 await 才能得到结果,所以我将其更改为 async Task
您必须使用delayTask.Wait();等待完成任务。
警告:delayTask.Wait()
是不好的做法,请改用 await delayTask;
。
使用这个代替你的代码我做了一些修改:
static void Main(string[] args) {
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(async () => await DoDelay(10000));
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
delayTask.Wait();
}
static async Task DoDelay(int delayMs)
{
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
你也可以使用
static async Task Main(string[] args)
并删除 Task.Run(async () => await DoDelay(10000));
并使用 await DoDelay(10000)
和 await delayTask;
但它可能无法正常工作 .NET Framework 4.6.2
Task delayTask = Task.Run(() => DoDelay(10000));
这将在后台线程上调用方法 DoDelay(10000)
,并且 return 是在方法 return 时完成的任务。但是,DoDelay
将在第一个 await
语句处 return。该方法的其余部分将在稍后 运行,但没有任何东西会等待它发生。
要解决此问题,您应该将方法 return 设为任务,即 async Task DoDelay(int delayMs)
。这不会影响方法 returns 的时间,但是 returned 任务让您等待整个方法完成。这可以通过使用 async main 方法变得更简单:
static async Task Main(string[] args) {
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
var delayTask = DoDelay(10000);
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
await delayTask;
Debug.WriteLine($"Main-C: {DateTime.Now:mm:ss.fff}");
}
static async Task DoDelay(int delayMs) {
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
仅在绝对必要时使用 async void
,即 UI 中的事件处理程序,然后确保您正在处理任何等待任务的失败。在绝大多数情况下,使用 async Task
或 async Task<T>
本例中 async/await
模式的正确实现如下面的代码
static class Program
{
static async Task Main(string[] args)
{
Console.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(async () => { await DoDelay(10000); });
Task delayTaskSync = Task.Run(() => { DoDelaySync(10000); });
Console.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
Task.WaitAll(delayTask, delayTaskSync);
}
static async Task DoDelay(int delayMs)
{
Console.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Console.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
static void DoDelaySync(int delayMs)
{
Console.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
Task.Delay(delayMs).Wait();
Console.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
}
在这里您可以看到同时具有异步函数和同步函数的实现。
Task.Run()
运行 是一个并行任务,在该任务中您可以使用更多任务 async/await
或使用同步函数。
我希望有人能帮助我理解一些看似简单的代码。显然,我错过了一些东西。关于这段代码的行为,有两点我不明白:
如果我让代码 运行,'End Delay' 的最后一个
Debug.WriteLine
永远不会被写入。这是为什么?如果我在 Main() 的
,此行为都不会改变Task.WaitAll()
行上放置一个断点,我看到delayTask
的状态为WaitingToRun
并且 'Begin Delay'Debug.WriteLine
inDoDelay()
不会发生,无论我等多久。但是,一旦我在 Task.WaitAll() 行上按 F10,我就会看到 'Begin Delay' 出现在输出中。为什么Task.WaitAll()
行上的断点似乎甚至会阻止 开始 任务?无论我使用Task.WaitAll(delayTask)
还是await delayTask
.
我运行正在使用 .NET Framework 4.6.2,不幸的是,我别无选择。
感谢发帖的每一个人。我有上面第一个问题的答案 - 我将 'async void DoDelay()' 更改为 'async Task DoDelay()'。但是我仍然对第二个问题中的行为没有任何解释。
更新:正如 JonasH 向我指出的那样,在 VS 中处于中断模式时所有任务都会停止。
对于任何感兴趣的人,这篇文章对理解正在发生的事情有很大帮助:https://www.pluralsight.com/guides/understand-control-flow-async-await
static void Main(string[] args)
{
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(() => DoDelay(10000));
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
Task.WaitAll(delayTask);
}
static async void DoDelay(int delayMs)
{
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
一些注意事项:避免使用 async void 因为你必须 await 才能得到结果,所以我将其更改为 async Task
您必须使用delayTask.Wait();等待完成任务。
警告:delayTask.Wait()
是不好的做法,请改用 await delayTask;
。
使用这个代替你的代码我做了一些修改:
static void Main(string[] args) {
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(async () => await DoDelay(10000));
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
delayTask.Wait();
}
static async Task DoDelay(int delayMs)
{
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
你也可以使用
static async Task Main(string[] args)
并删除 Task.Run(async () => await DoDelay(10000));
并使用 await DoDelay(10000)
和 await delayTask;
但它可能无法正常工作 .NET Framework 4.6.2
Task delayTask = Task.Run(() => DoDelay(10000));
这将在后台线程上调用方法 DoDelay(10000)
,并且 return 是在方法 return 时完成的任务。但是,DoDelay
将在第一个 await
语句处 return。该方法的其余部分将在稍后 运行,但没有任何东西会等待它发生。
要解决此问题,您应该将方法 return 设为任务,即 async Task DoDelay(int delayMs)
。这不会影响方法 returns 的时间,但是 returned 任务让您等待整个方法完成。这可以通过使用 async main 方法变得更简单:
static async Task Main(string[] args) {
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
var delayTask = DoDelay(10000);
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
await delayTask;
Debug.WriteLine($"Main-C: {DateTime.Now:mm:ss.fff}");
}
static async Task DoDelay(int delayMs) {
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
仅在绝对必要时使用 async void
,即 UI 中的事件处理程序,然后确保您正在处理任何等待任务的失败。在绝大多数情况下,使用 async Task
或 async Task<T>
本例中 async/await
模式的正确实现如下面的代码
static class Program
{
static async Task Main(string[] args)
{
Console.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(async () => { await DoDelay(10000); });
Task delayTaskSync = Task.Run(() => { DoDelaySync(10000); });
Console.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
Task.WaitAll(delayTask, delayTaskSync);
}
static async Task DoDelay(int delayMs)
{
Console.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Console.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
static void DoDelaySync(int delayMs)
{
Console.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
Task.Delay(delayMs).Wait();
Console.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
}
在这里您可以看到同时具有异步函数和同步函数的实现。
Task.Run()
运行 是一个并行任务,在该任务中您可以使用更多任务 async/await
或使用同步函数。