目录
一、什么是异步编程
近些年,.NET增加了很多新特新,异步编程就是期中之一。因为.Net的异步编程模型把复杂的异步编程变得简单易用。使得开发人员可以轻松开发出更高效的应用程序
为什么要使用异步编程?
就拿点餐来说,我们去餐馆里面吃饭,一般都是我们进去找位置坐下,然后服务员拿着菜单过来,让我们进行点餐,同时服务员在旁边等待,等我们点完了,再拿给后厨进行炒菜,随后服务下一位客人,这一种方式称为同步点餐。第二种就是我们进去坐下,服务员过来会给我们一个餐单,让我们自己点,同时下一个客人进来,她会重复同样的操作,把菜单给顾客,等到我们点完了,再招呼她过来拿走菜单,这称为异步点餐,也就是要说的异步编程。前者服务员会一直在你身边,这样会浪费服务员等待的时间,并且当店里忙的时候,别的顾客会不爽。而后者则充分利用了服务员等待的这段时间,让这段时间去干另外的事情,服务更多的顾客,尽管服务一个人的总时间有可能会增加,但覆盖范围非常全面。
二、async和await的基本使用
异步方法:用async关键字修饰的一种方法。
(1)异步方法的返回值一般是Task<T>,T是真正的返回值类型,如Task<int>,惯例:异步方法名字一般以Async结尾
(2)即使方法没有返回值,也最好把返回值声明为非泛型的Task
(3)调用异步方法时,一般在方法面前加await,这样返回值类型就是泛型指定的T类型
(4)异步方法的“传染性”:一个方法中如果有await调用,则这个方法必须用async修饰
异步方法的使用:
static async Task Main(string[] args)
{
await DownloadAsync("https://www.taobao.com",@"d:\study core\1.txt");
Console.WriteLine("ok");
}
static async Task DownloadAsync(string url, string filename)
{
using (HttpClient httpClient = new HttpClient()) {
string html = await httpClient.GetStringAsync(url);
await File.WriteAllTextAsync(filename, html);
}
}
注:如果同样的功能,既有同步方法,也有异步方法,优先使用异步方法
三、异步方法不等于多线程
static async Task Main(string[] args)
{
Console.WriteLine("之前ID: " + Thread.CurrentThread.ManagedThreadId);
await CalcAsync(5000);
Console.WriteLine("之后: " + Thread.CurrentThread.ManagedThreadId);
}
//n个随机数相加
static async Task<double> CalcAsync(int n)
{
Console.WriteLine("CalcAsync: " + Thread.CurrentThread.ManagedThreadId);
double result = 0;
Random random = new Random();
for (var i = 0; i < n*n; i++) {
result += random.NextDouble();
}
return result;
}
注:异步方法并不会自主的在新的线程当中去执行,只有调用Task.Run(),放到新的线程当中去执行。有的异步方法是没有标注async的,并且异步方法不要使用sleep(会阻塞总线程),如果要延时操作,请使用await Task.Delay();
四、CancellationToken参数
用于获得提前终止执行的信号
static async Task Main(string[] args)
{
CancellationTokenSource sts = new CancellationTokenSource();
sts.CancelAfter(1000);
CancellationToken cotken = sts.Token;
await DownloadAsync("https://www.taobao.com", 100, cotken);
}
static async Task DownloadAsync(string url, int n, CancellationToken cancellationToken)
{
using (HttpClient httpClient = new HttpClient())
{
for (int i = 0; i < n; i++)
{
string html = await httpClient.GetStringAsync(url);
Console.WriteLine($"{DateTime.Now}:{html}");
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("请求被取消");
break;
}
}
}
}
将正在打印的网址内容在一秒后取消这个请求。
五.异步与yield
yield不仅能够简化数据的返回,而且可以让数据“流水线化”,从而提升整体的性能
在旧版本c#中,async方法中不能使用yield。但在之后的版本中,可以使用IAsyncEnumerable修饰这个方法,从而使得这个方法可以使用yield,但调用该方法的时候必须使用await,如图:
static async Task Main(string[] args)
{
await foreach (var i in Test2()) {
Console.WriteLine(i);
}
}
static async IAsyncEnumerable<string> Test2()
{
yield return("wdqdw");
yield return ("okok");
yield return ("51268");
}
运行结果:wdqdw
okok
51268