C# 并发与多线程:进阶篇
在现代 C# 开发中,并发与多线程不仅仅是扩展能力,更是构建快速、响应灵敏且可扩展应用程序的基础工具。在进阶层面,我们超越了简单的 Task.Run 调用,需要面对调度器的细微之处、上下文同步以及处理共享数据的模式。本文将深入剖析 C# (.NET) 中关键的进阶并发概念,帮助您编写高效且安全的代码。
1. Task 与 ValueTask:深入使用
Task 类是 C# 异步编程的基础。然而,在进阶层面,理解 Task 与 ValueTask 的区别以及管理任务的生命周期至关重要。
何时使用 ValueTask?
ValueTask 是一种结构体,当结果已同步可用时,可以避免为 Task 对象分配内存。这对于高性能场景(例如缓存方法)至关重要。
public class CacheService{ private readonly Dictionary<string, string> _cache = new();
public async ValueTask<string> GetDataAsync(string key) { if (_cache.TryGetValue(key, out string value)) { // 结果已存在,不创建 Task return value; }
// 异步获取数据 string data = await FetchFromDatabaseAsync(key); _cache[key] = data; return data; }
private async Task<string> FetchFromDatabaseAsync(string key) { await Task.Delay(100); // 模拟延迟 return $"Data for {key}"; }}进阶错误处理与取消
使用 CancellationToken 正确取消长时间运行的操作。通过 CancellationTokenSource.CreateLinkedTokenSource 组合多个令牌。
public async Task ProcessWithTimeoutAsync(CancellationToken externalToken){ using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(externalToken, timeoutCts.Token);
try { await LongRunningOperationAsync(linkedCts.Token); } catch (OperationCanceledException) { Console.WriteLine("操作已取消或超时。"); }}2. SynchronizationContext 与任务调度器
理解 SynchronizationContext 对于处理 UI 线程(WPF、WinForms)和 ASP.NET Core 至关重要。在进阶层面,您可以创建自己的同步上下文。
为线程池配置上下文
默认情况下,await 会捕获当前的 SynchronizationContext。为避免此行为(并防止死锁),请使用 ConfigureAwait(false)。
public async Task ProcessDataAsync(){ // 此代码在原始上下文中执行(例如 UI) await Task.Run(() => HeavyComputation()).ConfigureAwait(false);
// 使用 ConfigureAwait(false) 后 —— 在线程池上下文中 // 无法访问 UI 元素! Console.WriteLine("在线程池中执行");}创建自定义调度器
您可以实现自己的 TaskScheduler,例如,用于限制同时执行的任务数量(类似于调度器级别的 SemaphoreSlim)。
public class LimitedConcurrencyTaskScheduler : TaskScheduler{ private readonly SemaphoreSlim _semaphore; private int _runningTasks;
public LimitedConcurrencyTaskScheduler(int maxConcurrency) { _semaphore = new SemaphoreSlim(maxConcurrency);