Task与 async 和await关键字使用和探讨

基本概念: 

  • Task (任务): 在 .NET 中,Task 表示一个可能会在未来完成的操作,可以是异步的,也可以是同步的。Task<TResult> 是返回结果的任务,而 Task 是不返回结果的任务。
  • async 关键字: 标记一个方法为异步方法。此方法可以包含 await 关键字,用于异步等待其他任务。async 方法的返回类型通常是 TaskTask<TResult>,如果不返回任何值,也可以是 void(不过在实践中,void 仅限于事件处理程序)。
  • await 关键字: 用来暂停(等待方法)异步方法的执行,直到 Task 完成。当任务完成时,代码会继续执行。

async和await简单写法:

写一个简单的异步方法,用Task.Run模拟跑一个任务,用await关键字:来等待这个任务执行完成,

async 关键字: 标记一个方法为异步方法。


        public static async Task<int> DoSomethingAsync()
        {
            // 同步部分
            Console.WriteLine("Start");

            // 异步部分,假设这是一个耗时的I/O操作
            int result = await Task.Run(() =>
            {
                Thread.Sleep(10000); // 模拟长时间的操作
                return 42;
            });

            // 当异步任务完成后,恢复执行
            Console.WriteLine("End");

            return result;
        }


        public static DateTime GetDate() { 
        
            return DateTime.Now;
        }
调用异步方法:

调用异步方法必须是用await来等待,该方法执行结束,不然无法调用,使用await之后必然要使用async标记调用方法要不然会报错,且返回值必须是用Task关键字处理异步任务并且返回一个 Task 对象,要不然也会报错。

     static  async Task Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
          int result = await  DoSomethingAsync();
            Console.WriteLine($"Result: {result}");
           var date=  GetDate();
            Console.WriteLine("当前时间",date.ToString()) ;
        }
简单使用总结: 
  1. Main 方法在执行时,调用了 await DoSomethingAsync()
  2. 此时,Main 方法暂停执行,等待 DoSomethingAsync() 完成。
  3. DoSomethingAsync() 执行完毕并返回结果后,await 会恢复 Main 的执行,继续运行后面的代码。

await和线程的关系:

await调用的等待期间,.net会把当前的线程返回给线程池,等待异步方法调用执行完毕后,框架会从线程池在取出来一个线程执行后续代码。

运行下面的简单的一个文件写入代码,调用await后线程id已经改变了

    static  async Task Main(string[] args)
        {
            //打印当前线程id
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 10000; i++)
            {
                sb.Append("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");

            }
            await File.WriteAllTextAsync(@"D:\移动\asp.net core\task和asnyc、await\test\1.txt",sb.ToString());
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }

执行结果:

异步方法不等于多线程:

异步方法的代码并不会自动在新线程中执行,除非把代码放到新线程中执行。

 写一个异步方法但里面没有用到await关键字

虽然这个方法是异步的 (async),但它没有切换线程,仍然在原来的线程上执行。这是因为 async 并不会自动创建新线程。

        //虽然看起来是异步的 但是方法体内并没有创建新线程 所以还是在原来的线程上运行
        public static async Task<double> CalcAsync(int n)
        {

            Console.WriteLine("CalcAsync" + Thread.CurrentThread.ManagedThreadId);
            double result = 0;
            Random random = new Random();
            for (int i = 0; i < n * n; i++)
            {
                result += random.NextDouble();
            }
            return result;
        }
  • async 关键字的主要作用是允许你在方法内部使用 await 来等待异步操作的完成,但它本身并不创建新线程
  • 如果没有 await 或者没有明确的异步操作,整个方法会像普通的同步方法一样执行,只不过它是以异步的形式返回 Task
  • 只有当你使用了 await 等待某个异步任务时,才会暂停当前方法的执行,等待该异步任务完成后继续执行。

 

 调用上面的异步方法

     static  async Task Main(string[] args)
        {


            Console.WriteLine("执行之前:"+Thread.CurrentThread.ManagedThreadId);
            double r = await CalcAsync(5000);

            Console.WriteLine("执行之后:" + Thread.CurrentThread.ManagedThreadId);
        }

执行结果:

线程id没有改变,因为异步方法不会自动在新线程中执行,想让异步方法在新的线程执行,必须手动设置到新的线程中执行。例如使用Task.Run。

       public static async Task<double> CalcAsync(int n)
        {

   
       return await     Task.Run(() =>{
                Console.WriteLine("CalcAsync" + Thread.CurrentThread.ManagedThreadId);
                double result = 0;
                Random random = new Random();
                for (int i = 0; i < n * n; i++)
                {
                    result += random.NextDouble();
                }
                return result;

            });
        }

但是Task.Run 不也会直接创建新线程,而是将任务提交给 .NET 线程池,并在池中某个空闲的线程上执行 ,这种方式叫做线程调度,方法将任务委托给线程池来执行, 是异步编程的基础之一。

Task类的是什么?

Task 类是 .NET 中表示异步操作的核心类。它代表一个将来会完成的操作(任务)。Task 可以异步执行,并且允许你等待任务的完成,获取返回值或处理异常。

有两种常用的 Task 类型:

Task:代表一个没有返回值的异步操作(类似于 void)。

Task<T>:代表一个异步操作,返回类型为 T 的结果。

例如:

处理多个并发任务

使用 Task.WhenAll 可以等待多个任务并发完成,这对于并行处理多个独立的异步操作非常有用。

写一个方法,模拟一个需要长时间执行的任务。

    static void DoWork(int taskId)
        {
            Console.WriteLine($"Task 任务{taskId} 正在执行...");
            Task.Delay(2000).Wait();  // 模拟长时间任务
            Console.WriteLine($"Task 任务{taskId}执行完成.");
        }

调用,并使用Task.WhenAll 对个任务并发进行处理。

   static  async Task Main(string[] args)
        {
            Task task1 = Task.Run(() => DoWork(1));
            Task task2 = Task.Run(() => DoWork(2));
            Task task3 = Task.Run(() => DoWork(3));

            await Task.WhenAll(task1, task2, task3); // 等待所有任务完成

            Console.WriteLine("All tasks completed.");

            Console.ReadKey();
        }

运行结果:

 WhenAll将方法三个方法一起并行执行完成。

Task.WhenAll 的使用场景

  1. 并行处理:当你有多个独立的任务且它们可以并行执行时,例如批量调用 Web API、并发读取文件等,可以使用 Task.WhenAll
  2. 批量执行并等待完成:在一个应用程序中,你可能需要执行多个异步操作并确保它们全部完成,比如启动多个数据库查询或网络请求,可以使用 Task.WhenAll 确保所有操作完成后再继续后续逻辑。
  3. 等待异步操作的集合:当你有一系列异步任务(可能数量动态变化)时,可以将这些任务打包在一起使用 Task.WhenAll 等待完成。
总结WhenAll方法
  • Task.WhenAll 是处理多个异步任务的强大工具,它不仅可以让你并行执行任务,还能确保所有任务完成后再继续执行后续逻辑。
  • 它还能够聚合多个任务的结果,适合并行操作的场景。
  • 对于异常处理,Task.WhenAll 会将所有任务中的异常一起处理,方便你处理多个异常场景。

通过 Task.WhenAll,你可以轻松管理并行任务和异步操作,提升应用的并发处理能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值