c#异步操作 の await
1.线程和进程
- 任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行。
- 任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很小的开销和精确的控制。
2.Task
Thread线程也是对象,频繁的创建和销毁线程比较影响性能,.NET提供线程池使得我们能够复用线程对象从而避免频繁创建和销毁线程。所以就提出了 ThreadPool。比Thread 占用更少的开支。
ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如:
- ThreadPool不支持线程的取消、完成、失败通知等交互性操作;
- ThreadPool不支持线程执行的先后次序;
以往,如果开发者要实现上述功能,需要完成很多额外的工作,现在,微软提供了一个功能更强大的概念:Task。Task在线程池的基础上进行了优化,所以说,Task 算是非常强大,并且方便的使用。
由于 Task 对象执行的工作通常在线程池线程上异步执行,而不是在主应用程序线程上同步执行,因此可以使用 Status 属性,还可以使用 IsCanceled、IsCompleted和 IsFaulted 属性,用于确定任务的状态。
3. await 和 async
使用 async 关键字可将方法、lambda 表达式或匿名方法标记为异步,即,方法中应该包含一个或多个await表达式,但async关键字本身不会创建异步操作。这里需要注意一点,若使用async关键字标记的方法中没有使用await关键字(编译器会给出警告但不报错),那么该方法将会以同步方式执行。
await使得我们使用异步操作时更加便捷,并且还不会导致线程堵塞(线程阻塞,简单点来理解,就是这个线程还没执行完呢,整个项目就停止了),
3.1 他为什么不会导致线程阻塞?
async方法在开始时以同步方式执行,在async方法内部,await关键字对它参数执行一个异步等待,它首先检查操作是否已经完成,如果完成,就继续运行(同步方式),否则,会暂停async方法,并返回.留下一个未完成的task,一段时间后,操作完成,async方法就恢复执行.
3.2 异步方法的几点要求
- 使用async关键字来修饰方法
- 在异步方法中使用await关键字(不使用编译器会给出警告但不报错),否则异步方法会以同步方式执行
- 尽量不使用void作为返回类型,若希望异步方法返回void类型,请使用Task
- 异步方法名称以Async结尾
- 异步方法中不能声明使用ref或out关键字修饰的变量
返回类型 void
不建议使用void作为异步方法的返回值。
因为使用Task或Task任务作为返回值,其属性携带有关其状态和历史记录的信息,如任务是否完成、异步方法是否导致异常或已取消以及最终结果是什么。而await运算符可访问这些属性。
4.Task.Delay
在异步编程中,一般不建议使用Thread.Sleep,而是使用粒度更小的Task.Delay;Thread.Sleep、Thread.Yeild等会让当前工作线程阻塞,而Task.Delay可以让当前线程空出来去完成其他的Task。
5.异常的捕获
需要注意的是,Task中的方法一般是不会将异常抛出的,哪怕是Task这种,这需要开发人员自行处理。不过如果获取Task.Result则会将任务内的异常抛出来;
public static async Task<string> Hello(int a=0)
{
return await Task.Run(async () =>
{
await Task.Delay(1000).ConfigureAwait(true);
return $"Hello {10/a}";
}).ConfigureAwait(true);
}
//不会报异常
Hello(0);
//会报异常
var a1= Hello(0).Result;
参考文献:
[1] https://www.cnblogs.com/yswenli/p/11987377.html
[2] https://www.cnblogs.com/haiyoune2/p/9243693.html