C#高级 02异步编程-LMLPHP

基础知识

1.什么是异步任务

  • 包含了异步任务的各种状态的一个引用类型
    1)正在运行、完成、结果、报错等
    2)另有ValueTask值类型版本
  • 对于异步任务的抽象
    1)开启异步任务后,当前线程并不会阻塞,而是可以去做其他事情
    2)异步任务(默认)会借助线程池在其他线程上运行
    3)获取结果后回到之前的状态
  • 任务结果
    1)返回值为Task的方法表示异步任务没有返回值
    2)返回值为Task则表示有类型为T的返回值

2.异步方法(async Task)

  • 将方法标记async后,可以在方法中使用await关键字
  • await关键字会等待异步任务,并获得结果
  • async+await会将方法包装成状态机,await类似于检查点(MoveNext方法会被底层调用,从而切换状态)
  • async Task
  • async void
  • 异步编程具有传染性

3.不阻塞

  • await会展示释放当前线程,使得该线程可以执行其他工作,而不必阻塞线程直到异步操作完成
  • 不要在异步方法里面用任何方式阻塞当前线程
  • 常见阻塞情况
    1)Task.Wait() & Task.Result()如果任务没有完成,则会阻塞当前线程,容易导致死锁
    2)Task.Delay() 是一个异步任务,会立刻释放当前线程
    3)Thread.Sleep() 会阻塞当前的线程,这与异步编程的理念不符
    4)IO等操作的同步方法
    5)其他繁重且耗时的任务

4.同步上下文

  • 一种管理和协调线程的机制,允许开发者将代码的执行切换到特定线程
  • WinForms 与 WPF 拥有同步上下文(UI线程),而控制台程序默认没有
  • ConfigureAwait(false)
    1)配置任务通过await方法结束后是否会到原来的线程,默认为true
    2)一般只有UI线程会采用这种策略
  • TaskScheduler
    1)控制Task的调度方式和运行线程
    1.1)线程池线程Default
    1.2)当前线程CurrentThread
    1.3)单线程上下文 STAThread
    1.4)长时间运行线程LongRunning
    2)优先级、上下文、执行状态等

5.一发即忘

  • 调用一个异步方法,但是并不使用await或阻塞的方式去等待它的结果
  • 无法观察任务状态(是否完成、是否报错等)

二.常见误区

1异步是否一定是多线程?

  • 异步编程不必需要多线程来实现(时间片轮转调度)
  • 比如可以在单个线程上使用异步I/O或事件驱动的编程模型(EAP)
  • 单线程异步:自己定好计时器,到时间之前先去做别的事
  • 多线程异步:将任务交给不同的线程,并由自己来进行指挥调度

2.异步方法一定要写成 async Task?

  • async关键字知识用来配合await使用,从而将方法包装成状态机
  • 本质上任然是Task,只不过提供了语法糖,并且函数体中可以直接return Task的泛型类型
  • 接口中无法声明async Task

3.await一定会切换同步上下文?

  • 在使用await关键字调用并等待一个异步任务时,异步方法不一定会立刻来到新的线程上
  • 如果 await 了一个已经完成的任务(包括Task.Delay(0)),会直接获得结果

4.异步编程可以取代多线程

  • 异步编程和多线程有一定关系,但是两者并不是可以完全相互代替的

5.Task.Result一定会阻塞当前线程?

  • 如果任务已经完成,那么Task.Result可以直接得到结果

6.开启的异步任务一定不会阻塞当前线程?

await关键字不一定会立刻释放当前线程,所以如果调用的异步方法中存在阻塞(如 Thread.Sleep(0)),那么依旧会阻塞当前线程上下文对应的线程

01-01 03:34