基本概念:
- Task (任务): 在 .NET 中,
Task
表示一个可能会在未来完成的操作,可以是异步的,也可以是同步的。Task<TResult>
是返回结果的任务,而Task
是不返回结果的任务。- async 关键字: 标记一个方法为异步方法。此方法可以包含
await
关键字,用于异步等待其他任务。async
方法的返回类型通常是Task
或Task<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()) ;}
简单使用总结:
Main
方法在执行时,调用了await DoSomethingAsync()
。- 此时,
Main
方法暂停执行,等待DoSomethingAsync()
完成。 DoSomethingAsync()
执行完毕并返回结果后,await
会恢复Main
的执行,继续运行后面的代码。
await和线程的关系:
await调用的等待期间,.net会把当前的线程返回给线程池,等待异步方法调用执行完毕后,框架会从线程池在取出来一个线程执行后续代码。
运行下面的简单的一个文件写入代码,调用await后线程id已经改变了
static async Task Main(string[] args){//打印当前线程idConsole.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
的使用场景
- 并行处理:当你有多个独立的任务且它们可以并行执行时,例如批量调用 Web API、并发读取文件等,可以使用
Task.WhenAll
。 - 批量执行并等待完成:在一个应用程序中,你可能需要执行多个异步操作并确保它们全部完成,比如启动多个数据库查询或网络请求,可以使用
Task.WhenAll
确保所有操作完成后再继续后续逻辑。 - 等待异步操作的集合:当你有一系列异步任务(可能数量动态变化)时,可以将这些任务打包在一起使用
Task.WhenAll
等待完成。
总结WhenAll方法
Task.WhenAll
是处理多个异步任务的强大工具,它不仅可以让你并行执行任务,还能确保所有任务完成后再继续执行后续逻辑。- 它还能够聚合多个任务的结果,适合并行操作的场景。
- 对于异常处理,
Task.WhenAll
会将所有任务中的异常一起处理,方便你处理多个异常场景。
通过 Task.WhenAll
,你可以轻松管理并行任务和异步操作,提升应用的并发处理能力。