【问题标题】:Async/Await vs Threads Scenario异步/等待与线程方案
【发布时间】:2016-07-24 20:34:35
【问题描述】:

我是 C# 新手,想知道在哪些情况下使用 Async/Await 以及何时使用线程,因为我知道 Async/Await 不会创建新线程。

例如,如果我想在接下来的 10 小时内安排每小时打印 hello,应该使用以下两种打印方法中的哪一种?

    private async void PrintHelloAsync(int delay)
    {
        await Task.Delay(delay);
        Console.WriteLine("Hello");
    }

    private void PrintHelloThread(int delay)
    {
        Task.Run(async () =>
        {
            await Task.Delay(delay);
            Console.WriteLine("Hello");
        });
    }

我应该调用 PrintHelloAsync 10 次并增加延迟,还是调用 PrintHelloThread?

我是否正确使用了这些并发概念?

现在我正在做一个项目,我需要在下午 6 点、晚上 9 点和午夜安排任务,并试图找出最好的方法。

【问题讨论】:

  • 这两个函数基本上是一回事,只是不能等待第二个。 PrintHelloThread 只会打印一次“Hello”。
  • 谁说异步等待不创建新线程?异步方法应该做一些异步工作。只要遇到 await 语句,调用线程就会继续执行。可能会创建一个新线程(取决于线程池)来执行异步方法的其余过程。

标签: c# multithreading asynchronous concurrency async-await


【解决方案1】:

这两个函数的主要区别在于第一个由调用线程执行,而第二个由线程池中的空闲线程调用。

stackoverflow 上的某个人曾经用这个比喻解释了我的区别(唉,我不再知道他的名字了)。

假设你想做早餐。你需要烤一些面包和煮一些鸡蛋。

你可以在烤面包机里放一些面包,等面包烤好了,把烤好的面包从烤面包机里拿出来,开始烧水。一旦水沸腾,你加入鸡蛋,等待一段时间,将鸡蛋从水中取出,然后熄火。这是一个典型的同步执行例子。

另一方面,将面包放入烤面包机后,就可以开始烧水了。等到面包烤好或水沸腾。在第一种情况下,您将取出面包并等待水沸腾。在后一种情况下,您将鸡蛋放入水中,然后等到面包烤好或鸡蛋准备好。一旦您必须等待某事发生,您就会环顾四周是否可以做一些其他事情来加快进程:异步执行的典型示例,但仍由一个线程执行。这是 async-await 使用的方法,你的第一个函数使用的方法。

最昂贵的方法是雇一个厨师,你会点他烤一些面包,同时你开始加热水煮一些鸡蛋:由多个线程执行并发处理。

如果您有一个线程只有一件事要做,那么如果该线程聘请厨师来做这件事并让该线程等到厨师准备好,那是不明智的:为什么不让线程来做这件事呢?

因此,如果您的线程无事可做,请使用您的第一个函数。

但是,只有当您的线程在异步函数本身中运行时,才能调用第一个函数。如果您的线程不在异步函数中,并且在烤面包时它可以做一些有意义的事情,请考虑使用第二种方法。

【讨论】:

    【解决方案2】:

    我假设您的最终目标是将任务安排在下午 6 点、晚上 9 点等。如果不详细说明,您可能会走错路(延迟任务并考虑线程)。相反,我建议查看一个调度程序系统——比如Quartz.netHangFire——托管在 Windows 服务中,或者只是使用 Windows 任务调度程序在特定时间启动您的应用程序。

    如果你别无选择,只能自己实现一个调度系统,我会从 System.Timers.Timer 类开始:

    using System;
    using System.Threading.Tasks;
    using System.Timers;
    
    class Example
    {
        static void Main()
        {
            Timer timer = new Timer(1000);
            timer.Elapsed += async ( sender, e ) => await HandleTimer();
            timer.Start();
            Console.Write("Press any key to exit... ");
            Console.ReadKey();
        }
    
        private static Task HandleTimer()
        {
            Console.WriteLine("Timer event");
        }
    }
    

    【讨论】:

    • 不幸的是,工作中一切都被锁定了,我只能访问内部开发的库。
    • 您是否有理由不想/不能使用 Windows 任务调度程序?
    • 该任务需要用户事先输入,因此在下午 6 点所需的值将是这个,然后使用该值执行任务,晚上 9 点和午夜也是如此。这些值每天都会根据业务活动和用户方面的一些主观性而变化。
    • 如果没有所有细节,很难给出决定性的答案。通常,将输入值保存在某种数据存储中,这将允许您停止和重新启动进程/应用程序而不会丢失数据,并且还使您能够将数据收集过程与数据处理过程分开(预定过程)。
    猜你喜欢
    • 2013-02-15
    • 2020-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 2015-07-13
    相关资源
    最近更新 更多