一、同步操作与异步操作
同步操作是先完成工作再返回调用者。
异步操作的大部分工作是返回给调用者之后才完成的。
我们平常用的方法比如Thread.Sleep大部分是同步方法,异步方法需要并发创建,它们对调用者来说是并行的。异步方法通常都会非常迅速返回给调用者,因此它们也称作为非阻塞方法。
异步编程通常是在新的线程或任务上来执行耗时事件长的函数,从而实现并发。
二、异步和多线程的比较
1:异步和多线程是不同的概念
异步并不意味着多线程,也可能是单线程。异步默认使用线程池。多线程经常阻塞,而异步要求不阻塞
2:使用场景特点
多线程有以下几种特点:
1.适合CPU密集型任务(耗时)
2.长期运行的任务
3.提供更底层的控制,操作线程、锁、信号量
4.不便于传参与返回
5.线程的开销较大
异步的特点
1.适合IO密集型任务(频繁的API调用、读取本地文件等)
2.适合短小的任务
3.避免线程阻塞,提高系统响应能力
三、async和await的使用
1.声明了async的方法就可以使用await来实现异步操作, await 时会释放当前线程,等所 await 的 Task 完成时会从线程池中申请新的线程继续执行 await 之后的代码,这本来是为了解决异步操作(比如IO操作)霸占线程实际却用不到线程的问题。
2.因为异步方法的主要目的就是防止阻塞,而在异步方法中await前用的线程仍然是调用其的线程,故要在await之前避免阻塞,把可能阻塞的过程放在await后。
3.先看以下代码
var inputs = Enumerable.Range(1,10).ToArray();
var outputs = new List<int>();
foreach (var input in inputs)
{
outputs.Add(await Job(input));
}
Console.WriteLine("done");
async Task<int> Job(int number)
{
await Task.Delay(100);//模拟耗时
return number*2;
}
有这么一个方法Job,在耗时100毫秒后返回一个数值。outputs通过foreach循环获取数值,这个方式不太对,因为在循环中我们将会重复十次的耗时等待,这不是异步想要的效果,我们可以修改成如下:
var tasks = new List<Task<int>>();
foreach (var input in inputs)
{
tasks.Add(Job(input));
}
await Task.WhenAll(tasks);
outputs = tasks.Select(x => x.Result).ToList();
Console.WriteLine("done");