C# 问答 03:异步、并发与取消
041 async 方法一定会创建线程吗?
难度: 基础
查看参考答案
结论: 不会,async 是状态机和 continuation 模型,I/O 等待无需占用线程
原因: CPU 工作仍在执行线程上,Task.Run 才请求线程池调度
边界: 是否切线程取决于 await 完成状态和调度上下文
042 为什么异步要一直传递到调用链顶部?
难度: 基础
查看参考答案
结论: 中途 Result 或 Wait 会阻塞线程并可能死锁或造成线程池饥饿
原因: 端到端 await 才能释放等待资源
边界: 同步入口确实需要桥接时应隔离边界而不是层层阻塞
043 Task.WhenAll 如何处理多个异常?
难度: 基础
查看参考答案
结论: 组合任务记录所有输入失败,await 通常重新抛其中一个代表异常
原因: 诊断时应检查组合任务或各输入任务
边界: 它不自动重试、限流或取消其他任务
044 async void 为什么仅适合事件?
难度: 基础
查看参考答案
结论: 调用方不能等待完成、组合结果或正常观察异常
原因: 事件签名要求 void 是主要例外
边界: 普通命令应返回 Task,即使没有结果
045 ConfigureAwait(false) 改变了什么?
难度: 基础
查看参考答案
结论: 它让 continuation 不要求回到捕获的同步上下文
原因: 不阻止 ExecutionContext 流动,也不保证新线程
边界: 通用库可使用,应用 UI 层是否使用取决于后续线程亲和需求
046 如何区分超时和用户取消?
难度: 基础
查看参考答案
结论: 使用不同 CancellationTokenSource 并保留来源,捕获异常时比较令牌
原因: 两者可能都表现为 OperationCanceledException
边界: 日志和 API 状态码应按来源分类
047 取消为何是协作式的?
难度: 进阶
查看参考答案
结论: Cancel 只发出请求,代码需观察 token 或调用可取消 API
原因: 强制终止可能破坏不变量和资源清理
边界: CPU 循环应定期检查,I/O 应把令牌传到底层
048 取消注册为什么要释放?
难度: 进阶
查看参考答案
结论: 注册持有回调及目标引用,生命周期过长会泄漏
原因: 取消和 Dispose 可能并发,应遵守 Register 的同步保证
边界: 短生命周期用 using 管理 CancellationTokenRegistration
049 lock 中为什么不能 await?
难度: 进阶
查看参考答案
结论: Monitor 与线程和同步作用域相关,挂起后无法安全持有
原因: 异步互斥使用 SemaphoreSlim.WaitAsync 等
边界: 获得后在 finally Release,并避免锁内执行不受控外部代码
050 Interlocked 适合什么场景?
难度: 进阶
查看参考答案
结论: 单个位置的计数和简单状态机原子更新
原因: 多字段不变量无法由一个原子操作自动保证
边界: 复杂状态可用锁、不可变快照加 CAS 或 Channel
051 volatile 能替代锁吗?
难度: 进阶
查看参考答案
结论: 通常不能,它保证特定读写可见性但不使复合操作原子
原因: value++ 仍是读改写竞态
边界: 标志位也应优先用 Volatile.Read Write 或适当同步并证明算法
052 SemaphoreSlim 如何做并发限制?
难度: 进阶
查看参考答案
结论: 进入前 WaitAsync,成功后在 finally 中 Release
原因: 限制的是同时执行数量,不是请求速率
边界: 取消发生在获得许可前时不能错误 Release
053 Channel 如何提供背压?
难度: 进阶
查看参考答案
结论: 有界 Channel 在满时按 FullMode 等待或丢弃
原因: 策略必须匹配消息是否允许丢失
边界: 生产者结束时 TryComplete,消费者观察完成和异常
054 Parallel 与 async 如何选择?
难度: 进阶
查看参考答案
结论: CPU 可分割计算考虑 Parallel,I/O 等待考虑 async
原因: 混合工作需分别限制 CPU 并行度和下游并发
边界: 并行不是越大越好,应按核心数和资源容量测量
055 PLINQ 为什么可能更慢?
难度: 实战
查看参考答案
结论: 分区、调度、合并和保持顺序都有开销
原因: 小集合、轻量算子或共享锁会抵消收益
边界: 只在代表数据上比较顺序 LINQ 与 PLINQ
056 ValueTask 的使用约束是什么?
难度: 实战
查看参考答案
结论: 默认按单次消费处理,不缓存、不并发 await
原因: IValueTaskSource 后端可复用,重复消费不安全
边界: 需要多次等待时转 Task,并确认性能收益值得复杂度
057 异步流如何传递取消?
难度: 实战
查看参考答案
结论: 迭代器用 EnumeratorCancellation 接收 WithCancellation 的令牌并传给内部等待
原因: 仅在外层写令牌而生产者忽略它不会停止
边界: 退出 await foreach 时会 DisposeAsync 枚举器
058 后台任务如何安全停止?
难度: 实战
查看参考答案
结论: 持有取消源,停止接收新工作,等待在途任务并释放资源
原因: 设置布尔变量后立即退出可能丢数据
边界: 为关闭设置截止时间并记录未完成工作
059 如何避免线程池饥饿?
难度: 实战
查看参考答案
结论: 避免在请求线程同步阻塞异步 I/O,限制无界并发和长时间 CPU 工作
原因: 线程池能扩容但速度和资源有限
边界: 用运行时计数器观察队列长度、线程数和延迟
060 怎样测试并发代码?
难度: 实战
查看参考答案
结论: 测试可观察不变量、取消、超时和失败,并用可控同步点制造交错
原因: 重复跑很多次不能替代确定性协调
边界: 压力测试和生产遥测用于补充而不是代替单元测试