C#-Async关键字(异步⽅法)
async关键字(异步⽅法)
async关键字是C#特有的。Java没有这玩意。
async在C#世界⾥是上下⽂关键字。它只有在修饰⼀个⽅法的时候才⾃动被编译器识别为关键字,在代码的其他位置上可以被⽤作变量名等其他任何⽤途。
async关键字⽤来修饰两类⽅法: lambda表达式或者异步⽅法。
拥有async修饰的⽅法称为async⽅法,⽐如:
public async Task<int>ExampleMethodAsync()
{
// (1) To do some code
/ (2) To do
// (3) To do some code here after
}
就如上⾯这个⽅法ExampleMethodAsync(),微软爷爷特别喜欢在定义异步函数名字后习惯加个Async后缀(这不是必须的,加不加编译器既不会报错,也不会影响异步特性),告诉我们这个⽅法是个异步⽅法。我们在⾃⼰定义异步⽅法的时候,也可以照搬这个微软的习惯。async修饰的⽅法内部,应当出现⼀个await关键字,两个关键字⼀般成对出现。当然,如果我们不⼩⼼忘记写await表达式或者语句,这
个async⽅法默认按照同步⽅式运⾏,同时,编译器会友好地提⽰我们是不是漏写了await。此外,async⽅法内部,可以有多个await语句。
awiat运⾏的语句,⼀般都是⽐较费时的任务(也就是会阻塞主线程的⼀些操作,⽐如获取Http应答,写⼊⽂档,保存数据库等),要不然就不需要异步了。
以上⾯的例⼦为例(假设例⼦中的await是第⼀个await),异步⽅法执⾏过程(⽐较粗的看):
1. 主线程进⼊⽅法ExampleMethodAsync()后,先顺序执⾏(1); 如果(1)当中有创建Task或Task<TResult>的语句,或者是调⽤其
他async⽅法(返回值是Task后者Task<TResult>),为了描述⽅便,我们都称为Task创建语句; ⽐如直接创建⼀个Task或
Task<TResult>:
var tsk = Task.Run(()=>{
Thread.Sleep(1000);
Console.Writeline("Do another job asynchronously.");
});
或者调⽤另外⼀个async⽅法:
Task<string> getStringTask = client.GetStringAsync("docs.microsoft/dotnet");
那么,在调⽤完Task创建语句的时候异步任务就已经开始运⾏了(这个语句调⽤本⾝是在主线程当中,内部的任务则是新的线程中执⾏),也就是此时异步的线程已经启动了,由于它是异步启动的,所以它并不会阻⽌主线程继续往下⾛;
2. 接下来,主线程会顺序运⾏到async⽅法内部的第⼀个await,如果第⼀个await调⽤的仍然是⼀个async⽅法,那么主程序继续进⼊这
个⽅法执⾏,⼀直到碰到⼀个await task为⽌,主线程才会跳出ExampleMethodAsync⽅法; 举个例⼦:
static void Main(string[] args)
{
//
ExampleMethodAsync();
// do
}
public static async void ExampleMethodAsync()
{
// (1) 执⾏⼀些任务Do2Async()前准备的事情...
await Do2Async();// (2)
await和async使用方法// (3) 运⾏⼀些Do2Async()执⾏完之后的事情...
}
public static Task Do2Async()
{
// 执⾏⼀些t任务执⾏前的事情,⽐如任务的准备...
Task t = Task.Run(()=>{
// 异步任务中执⾏费时的事情...
});
// 运⾏⼀些与t⽆关的事情...
await t;
// 在这⾥执⾏⼀些t任务执⾏完相关的事情...
}
调⽤⽅(也就是main所在的主线程)会⼀直执⾏到20⾏才跳出ExampleMethodAsync()⽅法,⽽不是在第10⾏。
4. ExampleMethodAsync()⽅法中剩余的(3)在执⾏完await(2)部分的内容才执⾏。
5. 假设ExampleMethodAsync()中有第⼆个,第三个…awiat,因为主程序已经跳出来了,后续的await会在异步线程中按顺序执⾏下
去。
async⽅法可以是下⾯三种返回类型:
Task
Task<TResult>
void 这种返回类型⼀般⽤在event事件处理器中,或者⽤在你只需要任务执⾏,不关⼼任务执⾏结果的情况当中。
任何其他具有GetAwaiter⽅法的类型(从C#7.0开始)
注意,我们⽆法等待(awiat)⼀个async void ⽅法。
using System;
using System.Threading.Tasks;
using System.Threading;
namespace test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} Hello, I am Caller!");
DoAsync();
Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} Hello, I am Caller too!");
Console.Read();
}
public static async void DoAsync()
{
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} In DoAsync(), before SunAsync()");
await SunAsync();
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} After SunAsync(), DoAsync() End.");
}
public static async Task SunAsync()
{
var t = Task.Run(()=>{
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} New Task~");
for(int i=0; i<10; i++)
{
Thread.Sleep(1000);
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} I am ");
}
});
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} After Task, before await.");
await t;
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} After await, before SunAsync() exit.");
}
}
}
此时的结果:
ThreadID:1 Hello, I am Caller!
ThreadID:1 In DoAsync(), before SunAsync()
ThreadID:1 After Task, before await.
ThreadID:4 New Task~
ThreadID:4 I am
ThreadID:4 I am
ThreadID:4 I am
ThreadID:4 I am
ThreadID:4 I am
ThreadID:4 I am
ThreadID:4 I am
ThreadID:4 I am
ThreadID:4 I am
ThreadID:4 I am
ThreadID:1 After await, before SunAsync() exit.
ThreadID:1 After SunAsync(), DoAsync() End.
ThreadID:1 Hello, I am Caller too!
仔细阅读这段代码和结果,细⼼体会,这段代码是ansync void⽅法嵌⼊调⽤ansync Task⽅法。要注意体会,并不是说⼀遇到await主程序(ansync ⽅法的调⽤⽅)就⽴即退出DoAsync()⽅法,⽽是执⾏到33⾏,碰到了第⼀个的Task才跳出来。 从这个例⼦的输出ThreadID号中,可知,33⾏await之后的内容都是在新的线程(4线程)中运⾏的。⽽33⾏await之前的内容都在主线程(1线程)中运⾏。
如果将SunAsync()代码改为(await之前增加⼀个Thread.Sleep(150000)):
public static async Task SunAsync()
{
var t = Task.Run(()=>{
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} New Task~");
for(int i=0; i<10; i++)
{
Thread.Sleep(1000);
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} I am ");
}
});
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} After Task, before await.");
Thread.Sleep(15000);//主线程睡15秒
await t;
System.Console.WriteLine($"ThreadID:{Thread.CurrentThread.ManagedThreadId} After await, befor
e SunAsync() exit.");
}
ThreadID:1 Hello, I am Caller!
ThreadID:1 In DoAsync(), before SunAsync()
ThreadID:1 After Task, before await.
ThreadID:4 New Task~
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:4 I am playing game…
ThreadID:1 After await, before SunAsync() exit.
ThreadID:1 After SunAsync(), DoAsync() End.
ThreadID:1 Hello, I am Caller too!
因为,Task.Run()的任务在运⾏到await之前就结束了,因此,await后的内容仍然在主线程(1线程)中执⾏。这个例⼦告诉我们,如果任务在await之前就已经执⾏完毕,那么await后的内容仍然保留在原线程中执⾏。
总之,async⽅法调⽤⽅在碰到⼀个实际的await task的时候才退出async⽅法体。⼀般在await之前处
理与异步任务⽆关的事情(这部分代码是由异步⽅法的调⽤⽅所在的线程执⾏),await之后的代码则是处理异步任务处理完后的事情,因此这部分代码就可以处理与异步任务相关的事情(这部分⼀般来说是在新建的异步线程中执⾏的,除⾮在调⽤await之前任务就已经很快的执⾏完了,那么这部分内容也可能仍然在调⽤⽅线程中执⾏)。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论