C# async与await的使用说明

C# 异步编程提供了两个关键字,async 和await,这里说明下怎么用
C# 5 引入了一种简便方法,即异步编程。此方法利用了 .NET Framework 4.5 及更高版本、.NET Core 和 Windows 运行时中的异步支持。 编译器可执行开发人员曾进行的高难度工作,且应用程序保留了一个类似于同步代码的逻辑结构。 因此,你只需做一小部分工作就可以获得异步编程的所有好处。

本主题概述了何时以及如何使用异步编程,并包括指向包含详细信息和示例的支持主题的链接。
测试环境:vs2017,.Net Framework4.6.1。

用法
async 用在方法定义前面,await只能写在带有async标记的方法中。
注意await异步等待的地方,await后面的代码和前面的代码执行的线程可能不一样
async关键字创建了一个状态机,类似yield return 语句;await会解除当前线程的阻塞,完成其他任务

0X01 简单用法

测试代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace AsyncAndAwait_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("主线程ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId);
            AsyncTest();
        }

        static async void AsyncTest()
        {
            Console.WriteLine("*******Start************ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId);
            Task<int> taskA=  Print();
            Console.WriteLine("*********Middle**********ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId);
            int a = await taskA;
            Console.WriteLine("*********End**********ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId);
        }


        static Task<int> Print()
        {
            var tcs = new TaskCompletionSource<int>();
            var thrd = new Thread(() =>
            {
                Console.WriteLine("子线程ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId);
                for (int i = 0; i < 3; i++)
                {
                    Console.WriteLine("===========等待==========" + i);
                    System.Threading.Thread.Sleep(1000);
                }
                tcs.SetResult(99);

            });
            thrd.Start();
            return tcs.Task;
        }
    }
}

结果
在这里插入图片描述

这里的结果红框这个地方线程id变了,按照我的理解是异步调用await前面代码的线程和await后面代码的线程可能不一样

再看一个测试例子

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace AsyncAndAwait_Test
{
    class Program
    {
        /// <summary>
        /// async 和await 实践测试
        /// </summary>
        /// <param name="args"></param>
        static void Main(string[] args)
        {
            
            var tcs1 = new TaskCompletionSource<int>();
            var tcs2 = new TaskCompletionSource<int>();
            Console.WriteLine("主线程ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId);
            AsyncTest(tcs1,tcs2);

            Thread.Sleep(1000);
            tcs1.SetResult(100);

            Thread.Sleep(5000);
            tcs2.SetResult(100);
            Console.WriteLine("主线程结束ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId);
        }
        static async void AsyncTest(TaskCompletionSource<int> tcs1, TaskCompletionSource<int> tcs2)
        {
            Console.WriteLine("AsyncTest方法执行的线程id:" + Thread.CurrentThread.ManagedThreadId);
            //await后线程出现变化,异步执行(线程id不一样)
            int a = await Print();
            Console.WriteLine("11****await tcs3后的线程id:" + Thread.CurrentThread.ManagedThreadId);
            //await后的线程没有变化,是因为SetResult比较早,这里可以直接同步执行(我认为的)
            int b = await tcs1.Task;
            Console.WriteLine("22****await tcs1后的线程id:" + Thread.CurrentThread.ManagedThreadId);
            //await后线程出现变化,异步执行(线程id不一样)
            int c= await tcs2.Task;
            Console.WriteLine("33****await tcs2后的线程id:" + Thread.CurrentThread.ManagedThreadId);
        }


        static Task<int> Print()
        {
            var tcs3 = new TaskCompletionSource<int>();
            var thrd = new Thread(() =>
            {
                Console.WriteLine("子线程ManagedThreadId:" + Thread.CurrentThread.ManagedThreadId);
                for (int i = 0; i < 3; i++)
                {
                    Console.WriteLine("===========等待==========" + i);
                    System.Threading.Thread.Sleep(1000);
                }
                tcs3.SetResult(99);

            });
            thrd.Start();
            return tcs3.Task;
        }
    }
}

结果:
在这里插入图片描述
代码里面await用法,这里异步返回的是一个Task<int>类型,通过await等待Task执行完成后返回的是int,或者返回的是T模板

根据结果总结结论如下:
如果执行到await时TaskCompletionSource没有设置SetResult、SetCanceled、SetException中的一个则await后面的线程可能出现变化(异步操作)
如果执行到await时TaskCompletionSource有设置SetResult、SetCanceled、SetException中的一个则await后面的线程不变化(同步操作)

0X02 深入用法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace AsyncAwaitTest
{
    class Program
    {
        static void Main(string[] args)
        {

            Main1();
            Main2();
        }


        static void Main1()
        {
            AsyncMethod1();
            Console.WriteLine("Main finished!!!");
            //这里是为了处理Task启动一个后台线程的问题,主线程结束时,后台线程自动关闭
            Thread.Sleep(5000);
        }
        static async void AsyncMethod1()
        {
            Console.WriteLine("主线程id:" + Thread.CurrentThread.ManagedThreadId);


            //切换线程等待完成
            string res = await Task.Run<string>( () =>{
                Thread.Sleep(1000);
                Console.WriteLine("func thread id:" + Thread.CurrentThread.ManagedThreadId);
                return "hello";
            });

            Console.WriteLine("AsyncMethod finished!!!");

        }

        /// <summary>
        /// 
        /// </summary>
        static void Main2()
        {
            Console.WriteLine("===============================================");
            AsyncMethod2();
            Console.WriteLine("Main2 finished!!!");
            //这里是为了处理Task启动一个后台线程的问题,主线程结束时,后台线程自动关闭
            Thread.Sleep(5000);
        }
        /// <summary>
        /// 两个task同时执行
        /// </summary>
        static async void AsyncMethod2()
        {
            Task<string> t1 = GreetingAsync("Wang",500);
            Task<string> t2 = GreetingAsync("Codingriver");
            //两个异步task同时执行,直到所有task执行完 await才返回
            await Task.WhenAll(t1, t2);
            Console.WriteLine("result:::" + t1.Result + "    " + t2.Result);
        }




        static Task<string>GreetingAsync(string name,int time=1000)
        {
            return Task.Run<string>(()=>{
                Thread.Sleep(time);
                return Greeting(name);
            });
        }

        static string Greeting(string name)
        {
            return "Hello " + name;
        }
    }
}

结果:
在这里插入图片描述

异步的异常捕获需要注意的是要将异步await放在try catch块中,且返回Task泛型,大概是这个意思
捕获多个异步的异常例子:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace AsyncAwaitTest
{
    class Program
    {
        static void Main(string[] args)
        {
            TestMoreTaskException();
        }




        static async void TestMoreTaskException()
        {

            Task results=null;
            try
            {

                Task<string> t1 = GreetingAsync(null, 500);
                Task<string> t2 = GreetingAsync("Codingriver");
                //两个异步task同时执行,直到所有task执行完 await才返回
                await(results= Task.WhenAll(t1, t2));
                Console.WriteLine("TestMoreTaskException");


            }
            catch (Exception ex)
            {
                //AggregateException
                Console.WriteLine("Exception:::::::::" + ex.ToString());
                foreach (var item in results.Exception.InnerExceptions)
                {
                    Console.WriteLine("InnerException:::::::::" + item.ToString());
                }
            }

        }
        static Task<string> GreetingAsync(string name, int time = 1000)
        {
            return Task.Run<string>(() =>
            {
                Thread.CurrentThread.IsBackground = false;
                Thread.Sleep(time);
                throw new Exception("codingriver test");
                return string.Empty;
            });
        }
    }
}

结果:
在这里插入图片描述

参考文章
使用 Async 和 Await 的异步编程 (C#)

<think>好的,用户想要了解C#asyncawait关键字的使用示例,需要结合代码案例。首先,我得回忆一下相关的知识,确保自己理解正确。根据引用[4],async用来标记异步方法,await则用于等待异步操作完成,这样可以不阻塞主线程。 接下来,用户的问题是关于使用案例,所以需要提供具体的代码示例。引用[3]中有一个例子,展示了一个异步方法DoSomethingAsync,它返回Task,并在Task.Run中执行操作。然后在RunAsync方法中用foreach循环依次await每个任务。这可能是一个顺序执行的例子,但如果是并行的话,可能需要使用Task.WhenAll。 另外,引用[2]提到了几个常见的错误,比如死锁问题,当在UI线程调用.Wait()时可能导致死锁,这需要提醒用户注意使用ConfigureAwait(false)来避免。还有忘记await和过度使用async void的问题,这些也应该在注意事项中提到。 用户可能不仅需要基础示例,还可能想知道如何正确应用这些关键字,以及避免常见的陷阱。比如,在UI编程中,异步操作如何保持响应性,或者在Web API中处理并发请求。需要分不同场景给出例子。 然后,我需要组织这些信息,结构要清晰。先介绍asyncawait的基本用法,然后给出基础示例,比如异步下载数据或文件操作。接着,处理多个任务的情况,比如顺序执行和并行执行的区别。再提到常见的错误和解决方法,如死锁和忘记await。 还要确保代码格式正确,使用正确的语法。比如,异步方法返回Task或Task<T>,使用await调用。同时,注意在示例中展示正确的实践,比如在库代码中使用ConfigureAwait(false)来避免死锁。 可能还需要对比同步和异步方法的区别,让用户明白异步的优势。例如,在UI应用中,长时间操作不会冻结界面;在服务端提高吞吐量。 最后,总结使用时的注意事项,并生成相关问题,帮助用户进一步学习。比如,如何优化异步代码,或者在不同框架中的使用差异。</think>### C# async/await 使用示例解析 #### 一、基础用法 1. **异步方法定义** 使用 `async` 关键字修饰方法,并返回 `Task` 或 `Task<T>`。 ```csharp public async Task DownloadDataAsync(string url) { using HttpClient client = new HttpClient(); string result = await client.GetStringAsync(url); Console.WriteLine("下载完成"); } ``` 2. **await 等待异步操作** 在需要等待异步结果的语句前添加 `await`: ```csharp public async Task ProcessDataAsync() { await Task.Delay(1000); // 模拟耗时操作 Console.WriteLine("数据处理完成"); } ``` --- #### 二、多任务处理 1. **顺序执行任务** 通过 `await` 逐个执行异步操作: ```csharp public async Task RunSequentiallyAsync() { await DoTaskAsync(1); // 等待任务1完成 await DoTaskAsync(2); // 再执行任务2 } ``` 2. **并行执行任务** 使用 `Task.WhenAll` 实现并行执行: ```csharp public async Task RunParallelAsync() { Task task1 = DoTaskAsync(1); Task task2 = DoTaskAsync(2); await Task.WhenAll(task1, task2); // 同时等待所有任务完成 } ``` --- #### 三、实际场景案例 1. **UI 应用(避免界面冻结)** ```csharp private async void Button_Click(object sender, EventArgs e) { await Task.Run(() => { // 执行耗时计算(如图像处理) }); label.Text = "操作完成"; // 自动返回UI线程更新界面 } ``` 2. **Web API 异步处理请求** ```csharp public class DataController : ApiController { public async Task<IHttpActionResult> GetAsync() { var data = await FetchFromDatabaseAsync(); return Ok(data); } } ``` --- #### 四、常见错误解决 1. **死锁问题** 在同步上下文中直接调用 `.Wait()` 或 `.Result` 会导致死锁[^2]: ```csharp // 错误写法 public void CallAsyncMethod() { DeadlockDemoAsync().Wait(); // 阻塞主线程导致死锁 } // 正确解决:使用 ConfigureAwait(false) public async Task DeadlockDemoAsync() { await Task.Delay(1000).ConfigureAwait(false); } ``` 2. **忘记 await** 未使用 `await` 会导致异步操作未完成时继续执行后续代码: ```csharp public async Task ForgetAwaitDemoAsync() { DoSomethingAsync(); // 错误:未等待 Console.WriteLine("完成"); // 可能在异步操作前执行 } ``` --- #### 五、注意事项 1. **避免 `async void`** 仅在事件处理中使用 `async void`,其他场景应返回 `Task`。 2. **返回值处理** 异步方法返回值需用 `Task<T>` 包裹: ```csharp public async Task<int> CalculateAsync() { await Task.Delay(500); return 42; } ``` ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值