? 前言
C# 中的异步方法是 C#5.0 才有的新特性,可以在执行当前方法的同时异步的去执行其他方法。
? 什么是异步方法
1) 异步方法,是指在执行当前方法的同时,去执行其他异步方法,但不会阻塞当前方法的线程。
2) 使用了 async 修饰符的方法称为异步方法,通常配合 await 运算符和 Task 异步任务一起使用。
3) Task 分为两种:
1. Task,表示可以执行一个异步操作,声明如下:
public class Task : IAsyncResult, IDisposable { }
2. Task<TResult>,表示可以执行带有返回值的异步操作,声明如下:
public class Task<TResult> : Task { }
4) 异步方法的返回类型必须为 void、Task、Task<TResult> 中的其中一种。
1. void,表示无返回值,不关心异步方法执行后的结果,一般用于仅仅执行某一项任务,但是不关心结果的场景。
2. Task,表示异步方法将返回一个 Task 对象,该对象通常用于判断异步任务是否已经完成,可以使用 taskObj.Wait() 或者 taskObj.IsCompleted 判断。
3. Task<TResult>,表示异步方法将返回一个 Task<TResult> 对象,该对象的 Result 属性则是异步方法的执行结果,调用该属性时将阻塞当前线程(异步方法未执行完时)。
? 总结下:void 不关心结果;Task 只关心是否运行完成;Task<TResult> 不止关心是否运行完成,还要获得运行结果。
1. 通过几个例子来看下异步方法的特征
1) 代码如下:
public void AsyncTest()
{
ConsoleExt.WriteLine("AsyncTest() 方法开始执行,线程Id为:{0}", GetCurrentThreadId());
Test1();
ConsoleExt.WriteLine("AsyncTest() 方法结束执行,线程Id为:{0}", GetCurrentThreadId());
}
public async void Test1()
{
ConsoleExt.WriteLine("Test1() 方法开始执行,线程Id为:{0}", GetCurrentThreadId());
Task task = Task.Run(() =>
{
ConsoleExt.WriteLine("Test1() 方法中的 Task 开始执行,线程Id为:{0}", GetCurrentThreadId());
ConsoleExt.WriteLine("Test1() 方法中的 Task 结束执行,线程Id为:{0}", GetCurrentThreadId());
});
ConsoleExt.WriteLine("Test1() 方法结束执行,线程Id为:{0}", GetCurrentThreadId());
}
运行以上代码:
以上代码在 Test() 方法中,重新开启了一个新的 Task 去执行另一个任务,开启后当前线程并不会等等,则继续往下执行。
2) 调用 Task 的 Wait() 方法(阻塞线程)
Task task = Task.Run(() =>
{
ConsoleExt.WriteLine("Test1() 方法中的 Task 开始执行,线程Id为:{0}", GetCurrentThreadId());
ConsoleExt.WriteLine("Test1() 方法中的 Task 结束执行,线程Id为:{0}", GetCurrentThreadId());
});
task.Wait();
运行以上代码:
可见,调用 Wait() 方法后,当前线程被阻塞了,直到 Task 执行完成后,当前线程才继续执行。
3) 修改 Test1() 方法
public async void Test1()
{
ConsoleExt.WriteLine("Test1() 方法开始执行,线程Id为:{0}", GetCurrentThreadId());
await Task.Run(() =>
{
ConsoleExt.WriteLine("Test1() 方法中的 Task 开始执行,线程Id为:{0}", GetCurrentThreadId());
ConsoleExt.WriteLine("Test1() 方法中的 Task 结束执行,线程Id为:{0}", GetCurrentThreadId());
});
ConsoleExt.WriteLine("Test1() 方法结束执行,线程Id为:{0}", GetCurrentThreadId());
}
运行以上代码:
1. 以上代码在 Test() 方法上加了 async 关键字,并且使用 await 关键字等待异步任务执行完成,当前线程并没有阻塞。
2. 注意:当前线程遇到 await 时,则立刻回到调用方法继续往下执行。而 Task 执行完成之后将执行 await 之后的代码,并且与 await 之前的线程不是同一个。