睡眠真的会阻止执行吗?
Is Sleep really blocking the execution?
几乎所有关于 C# 异步编程的介绍都警告不要使用 Sleep 指令,因为它会阻塞整个线程。
但是我发现在睡眠期间,正在从队列中获取并执行任务。参见:
using System;
using System.Threading.Tasks;
namespace TestApp {
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main");
Program.step1();
for (int i = 0; i < 6; i++) {
System.Threading.Thread.Sleep(200);
Console.WriteLine("Sleep-Loop");
}
}
private static async void step1() {
await Task.Delay(400);
Console.WriteLine("Step1");
Program.step2();
}
private static async void step2() {
await Task.Delay(400);
Console.WriteLine("Step2");
}
}
}
输出:
Main
Sleep-Loop
Sleep-Loop
Step1
Sleep-Loop
Sleep-Loop
Step2
Sleep-Loop
Sleep-Loop
我的问题:
- Sleep 真的允许排队的任务执行,还是发生了其他事情?
- 如果是,那么其他所有闲置情况是否也会发生这种情况?例如在轮询期间?
- 在上面的例子中,如果我们注释掉循环,那么应用程序会在任何任务执行之前退出。还有其他方法可以防止这种情况发生吗?
在 C# 7.3 中,您可以拥有异步入口点,我建议使用它。
一些注意事项:
- 不要使用 async void,它在处理错误的方式上有微妙之处,如果你看到自己在写 async void 然后想想你在做什么。如果不是事件处理程序,你可能做错了什么
- 如果你想等待一堆任务完成,使用
Task.WhenAll
修改示例
static async Task Main(string[] args)
{
Console.WriteLine("Start Task");
var task = Program.step1();
for (int i = 0; i < 6; i++)
{
await Task.Delay(100);
Console.WriteLine("Sleep-Loop");
}
Console.WriteLine("waiting for the task to finish");
await task;
Console.WriteLine("finished");
Console.ReadKey();
}
private static async Task step1()
{
await Task.Delay(1000);
Console.WriteLine("Step1");
await Program.step2();
}
private static async Task step2()
{
await Task.Delay(1000);
Console.WriteLine("Step2");
}
重要的是要注意任务不是线程,async
不是并行的,但它们可以是。
如果您使用异步等待模式,那么 10 次中有 9 次是为了 IO 绑定工作使用操作系统 I/O 完成端口,这样您就可以释放线程。这是一种可扩展性和 UI 响应能力。
如果您没有做任何 I/O 工作,那么实际上根本不需要 async
await
模式,因此 CPU工作可能应该在调用时被包裹在 Task.Run
中。未包含在 async
方法中。
此时还需要注意的是,仅使用任务不是异步和等待模式。虽然他们都有共同的任务,但他们不是一回事。
最后,如果您发现需要以一种即用即忘的方式使用异步代码,请仔细考虑您将如何处理任何错误。
这里有一些指南。
- 如果您想做 I/O 工作,请使用异步等待模式。
- 如果您想做 CPU 工作,请使用 Task.Run。
- 切勿使用 async void 除非它用于事件处理程序。
- 永远不要在异步方法中包装 CPU 工作,让调用者使用
Task.Run
- 如果你需要等待任务,
await
它,永远不要调用Result
,或Wait
或使用Task.WhenAll
几乎所有关于 C# 异步编程的介绍都警告不要使用 Sleep 指令,因为它会阻塞整个线程。
但是我发现在睡眠期间,正在从队列中获取并执行任务。参见:
using System;
using System.Threading.Tasks;
namespace TestApp {
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Main");
Program.step1();
for (int i = 0; i < 6; i++) {
System.Threading.Thread.Sleep(200);
Console.WriteLine("Sleep-Loop");
}
}
private static async void step1() {
await Task.Delay(400);
Console.WriteLine("Step1");
Program.step2();
}
private static async void step2() {
await Task.Delay(400);
Console.WriteLine("Step2");
}
}
}
输出:
Main
Sleep-Loop
Sleep-Loop
Step1
Sleep-Loop
Sleep-Loop
Step2
Sleep-Loop
Sleep-Loop
我的问题:
- Sleep 真的允许排队的任务执行,还是发生了其他事情?
- 如果是,那么其他所有闲置情况是否也会发生这种情况?例如在轮询期间?
- 在上面的例子中,如果我们注释掉循环,那么应用程序会在任何任务执行之前退出。还有其他方法可以防止这种情况发生吗?
在 C# 7.3 中,您可以拥有异步入口点,我建议使用它。
一些注意事项:
- 不要使用 async void,它在处理错误的方式上有微妙之处,如果你看到自己在写 async void 然后想想你在做什么。如果不是事件处理程序,你可能做错了什么
- 如果你想等待一堆任务完成,使用
Task.WhenAll
修改示例
static async Task Main(string[] args)
{
Console.WriteLine("Start Task");
var task = Program.step1();
for (int i = 0; i < 6; i++)
{
await Task.Delay(100);
Console.WriteLine("Sleep-Loop");
}
Console.WriteLine("waiting for the task to finish");
await task;
Console.WriteLine("finished");
Console.ReadKey();
}
private static async Task step1()
{
await Task.Delay(1000);
Console.WriteLine("Step1");
await Program.step2();
}
private static async Task step2()
{
await Task.Delay(1000);
Console.WriteLine("Step2");
}
重要的是要注意任务不是线程,async
不是并行的,但它们可以是。
如果您使用异步等待模式,那么 10 次中有 9 次是为了 IO 绑定工作使用操作系统 I/O 完成端口,这样您就可以释放线程。这是一种可扩展性和 UI 响应能力。
如果您没有做任何 I/O 工作,那么实际上根本不需要 async
await
模式,因此 CPU工作可能应该在调用时被包裹在 Task.Run
中。未包含在 async
方法中。
此时还需要注意的是,仅使用任务不是异步和等待模式。虽然他们都有共同的任务,但他们不是一回事。
最后,如果您发现需要以一种即用即忘的方式使用异步代码,请仔细考虑您将如何处理任何错误。
这里有一些指南。
- 如果您想做 I/O 工作,请使用异步等待模式。
- 如果您想做 CPU 工作,请使用 Task.Run。
- 切勿使用 async void 除非它用于事件处理程序。
- 永远不要在异步方法中包装 CPU 工作,让调用者使用
Task.Run
- 如果你需要等待任务,
await
它,永远不要调用Result
,或Wait
或使用Task.WhenAll