睡眠真的会阻止执行吗?

时间:2019-03-10 00:59:27

标签: c# .net asynchronous mono

几乎所有有关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

我的问题:

  1. 睡眠是否真的允许执行排队的任务,或者发生其他事情?
  2. 如果是,那么是否在其他所有空闲情况下也会发生这种情况?例如在轮询期间?
  3. 在上面的示例中,如果我们注释掉循环,那么应用程序将在执行任何任务之前退出。还有另一种方法可以防止这种情况发生?

1 个答案:

答案 0 :(得分:2)

在C#7.3中,您可以有一个异步入口点,我建议使用它。

一些注意事项:

  1. 不要使用异步void,它在处理错误方面有一些微妙之处,如果您看到自己在写异步void,那么请考虑您在做什么。如果不是事件处理程序,则可能是您做错了事
  2. 如果您要等待一堆任务完成,请使用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绑定工作使用操作系统IO完成端口,以便释放线程。它具有可扩展性和ui响应功能。

如果您不做任何IO工作,那么实际上根本不需要async await模式,因此,这种cpu工作可能应该被包装在{{ 1}}。没有包装在Task.Run方法中。

在这一点上,还需要注意的是,仅使用任务不是异步和等待模式。尽管它们有共同的任务,但它们不是同一回事。

最后,是,如果您发现需要急于使用异步代码而忘记使用方式,请仔细考虑如何处理所有错误。

以下是一些准则。

  • 如果要执行IO工作,请使用异步等待模式。
  • 如果要执行CPU工作,请使用Task.Run。
  • 除非事件处理程序使用异步void,否则请勿使用它。
  • 永远不要以异步方法包装CPU,让调用者使用async
  • 如果您需要等待某个任务,Task.Run,请不要调用awaitResult或使用Wait