1.await/async简述
- await/async是C#保留关键字,通常成对出现
- async修饰方法,可以单独出现,对原来的方法没有任何改变,但会警告
- await在方法体,只能出现在task/async方法前面,单独使用await会报错
- async位于方法修饰符之后,返回类型之前
2.相关例子
2.1.单独使用async
- 代码如下:
/// <summary>
///方法带async,但方法体中Task前面不带await(与普通方法执行一样,只不过会警告)
/// </summary>
static async void AsyncNoAwait()
{
Console.WriteLine($"AsyncNoAwait start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Task.Run(() =>
{
Console.WriteLine($"AsyncNoAwait--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"AsyncNoAwait--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
Console.WriteLine($"AsyncNoAwait end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
- 在控制台输出如下:
- 结论:使用方式与普通方法无异,只是会有警告
2.2.await和async同时使用
await在方法内部位于Task前面,当前线程遇到await则跳出方法,然后继续执行,而await Task.Run()之后的代码都要等Task执行完成之后执行,效果与Task.Run()的回调一样
- 代码如下:
static void AsyncAwaitTest()
{
Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
//线程执行到AsyncWithAwait方法里面的await语句之后,跳出此方法,继续之后后面的代码
AsyncWithAwait();
Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// await/async同时使用
/// 此await的作用就是让当前线程不执行后面的代码,而是跳出方法体,去执行调用次方法后面的代码,
/// 而await后面的代码在await 异步任务执行完成之后调用,类似回调
/// </summary>
static async void AsyncWithAwait()
{
Console.WriteLine($"AsyncWithAwait start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
//执行到此处,当前线程就会跳出当前方法
await Task.Run(() =>
{
Console.WriteLine($"AsyncWithAwait--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"AsyncWithAwait--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
//此处的输出等上面的Task.Run 执行结束之后执行
Console.WriteLine($"AsyncWithAwait end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
- 控制台输出如下:
- 结论:当前线程1执行遇到await,不执行await后面的代码,而await Task.Run()新开一个线程3执行lambda,所以之后两个线程是并发执行的,互不干扰,只不过是先后顺序而已,至于Task.Run()后面的代码,是在Task.Run()执行完成之后执行的,可能会是当前线程也可能会新开一个线程
2.3.async方法上加多Task
- 代码如下:
static void AsyncAwaitTest()
{
Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
//线程执行到AsyncWithAwait方法里面的await语句之后,跳出此方法,继续之后后面的代码
Task task = TaskAsyncNoRerturn();
//wait会阻塞当前线程,直到TaskAsyncNoRerturn方法中await task后面的同步方法执行完成
task.Wait();
Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// 方法带Task,但方法没有返回值
/// 返回的Task包含了整个调用的方法,如果使用Task.wait(),将等待方法同步线程执行完毕
/// </summary>
/// <returns></returns>
static async Task TaskAsyncNoRerturn()
{
Console.WriteLine($"TaskAsyncNoRerturn start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Task task = Task.Run(() =>
{
Console.WriteLine($"TaskAsyncNoRerturn--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Console.WriteLine($"TaskAsyncNoRerturn--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
await task; //方法声明中的Task与当前task没有任何关系
//调用此方法,如果使用返回值task.wait()阻塞线程,将等到后面同步代码执行完毕才会跳过task.wait()
for (int i = 0; i < 2; i++)
{
Console.WriteLine($"{i} ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
Console.WriteLine($"TaskAsyncNoRerturn end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
- 控制台输出如下:
- 结论:在async方法中加入Task并不是指方法需要return,而是调用之后会自动返回一个Task类型的对象(暂时作为 task),调用task.wait()之后,会阻塞当前线程,直到async方法内部的同步代码执行完成
备注:async方法内部的同步代码:指的是在await Task.Run()之后的代码是没有再创建任何线程去执行的代码
2.4.带返回值的async方法
方法声明:访问修饰符 async Task<返回值类型> 方法名
例子:public async Task<int> Method
- 代码如下:
static void AsyncAwaitTest()
{
Console.WriteLine($"AsyncAwaitTest start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
Task<int> task = TaskAsyncWithRerturn();
//调用task.Result获取返回值,会阻塞当前线程,指定返回值计算完成
Console.WriteLine($"TaskAsyncWithRerturn的返回值:{task.Result}");
Console.WriteLine($"AsyncAwaitTest end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// TaskAsyncWithRerturn方法返回int类型的值
/// 调用TaskAsyncWithRerturn返回,会返回一个Task<int>对象
/// 调用者想要获取返回值,就得等task线程计算完毕,就是会阻塞调用线程
/// </summary>
/// <returns></returns>
static async Task<int> TaskAsyncWithRerturn()
{
Console.WriteLine($"TaskAsyncWithRerturn start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
int result = 0;
Task task = Task.Run(() =>
{
Console.WriteLine($"TaskAsyncWithRerturn--Task start ThreadId:{Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < 200; i++)
{
result += i;
Thread.Sleep(10);
}
Console.WriteLine($"TaskAsyncWithRerturn--Task end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
});
await task;
Console.WriteLine($"TaskAsyncWithRerturn end ThreadId:{Thread.CurrentThread.ManagedThreadId}");
return result;
}
- 控制台输出如下:
- 结论:调用带返回值的async方法,如果要获取其返回值,就会阻塞当前线程,直到async方法中的返回值计算完毕