文章目录
1、从生活比喻开始
场景:餐厅的异步服务
-
同步方式:服务员点完菜后,一直站在厨房门口等待,直到菜做好才服务下一桌
-
异步方式:服务员点完菜后,继续服务其他客人,厨房做好菜后通知服务员上菜
Async/Await 的本质:一种让异步代码写得像同步代码一样直观的语法糖,底层基于状态机和回调机制实现非阻塞执行。
2、Async/Await 的核心本质
2.1 状态机转换
编译器将 async 方法转换为一个状态机:
// 原始 async 方法
public async Task<int> GetDataAsync()
{
Console.WriteLine("开始获取数据");
string data = await DownloadStringAsync("http://example.com");
int length = data.Length;
Console.WriteLine($"数据长度: {length}");
return length;
}
// 编译器生成的状态机(概念性)
class GetDataAsyncStateMachine : IAsyncStateMachine
{
private int _state = -1; // 状态标识
private TaskAwaiter<string> _awaiter; // 等待器
private string _data; // 局部变量
private int _length; // 局部变量
public AsyncTaskMethodBuilder<int> _builder; // 任务构建器
void IAsyncStateMachine.MoveNext()
{
try
{
switch (_state)
{
case -1: // 初始状态 - await 之前的部分
Console.WriteLine("开始获取数据");
// 开始异步操作
Task<string> downloadTask = DownloadStringAsync("http://example.com");
_awaiter = downloadTask.GetAwaiter();
if (!_awaiter.IsCompleted) // 如果操作尚未完成
{
_state = 0; // 设置下一个状态
_awaiter.OnCompleted(MoveNext); // 注册完成回调
return; // 退出,让出控制权
}
// 如果已经完成,直接继续执行
goto case 0;
case 0: // await 完成后的状态
_data = _awaiter.GetResult(); // 获取异步结果
_length = _data.Length;
Console.WriteLine($"数据长度: {_length}");
_builder.SetResult(_length); // 设置最终结果
break;
}
}
catch (Exception ex)
{
_builder.SetException(ex); // 设置异常
}
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
_builder.SetStateMachine(stateMachine);
}
}
2.2 执行上下文流动`class ExecutionContextFlow
{
public async Task DemonstrateContextFlow()
{
// 执行上下文包含:安全上下文、文化信息、同步上下文等
Console.WriteLine($"原始线程: {Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"原始文化: {Thread.CurrentThread.CurrentCulture}");
// 默认情况下,执行上下文会流动
await Task.Run(() => {
Console.WriteLine($"延续线程: {Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"延续文化: {Thread.CurrentThread.CurrentCulture}");
});
// 使用 ConfigureAwait(false) 禁止上下文流动
await Task.Run(() => {
Console.WriteLine("没有上下文流动");
}).ConfigureAwait(false);
}
}`
3、底层原理深入解析
3.1 等待器(Awaiter)模式
async/await 基于通用的等待器模式,任何实现此模式的对象都可以被 await:
// 可等待模式的概念性定义
public interface IAwaiter<out T> : INotifyCompletion
{
bool IsCompleted { get; }
T GetResult();
}
public interface IAwaitable<out T>
{
IAwaiter<T> GetAwaiter();
}
// Task 的实现
public class Task<T> : IAwaitable<T>
{
public TaskAwaiter<T> GetAwaiter()
{
return new TaskAwaiter<T>(this);
}
}
public struct TaskAwaiter<T> : IAwaiter<T>
{
private readonly Task<T> _task;
public TaskAwaiter(Task<T> task)
{
_task = task;
}
public bool IsCompleted => _task.IsCompleted;
public T GetResult()
{
return _task.Result; // 阻塞直到任务完成
}
public void OnCompleted(Action continuation)
{
// 注册回调,当任务完成时执行 continuation
_task.ContinueWith(_ => continuation());
}
}
3.2 同步上下文(SynchronizationContext)
class SynchronizationContextDemo
{
// 在 UI 应用程序中的同步上下文
public async Task UpdateUIAsync()
{
// 在 UI 线程执行
textBox.Text = "开始下载...";
// await 默认会捕获同步上下文
string data = await DownloadDataAsync();
// 这里会回到原始的 UI 线程执行
textBox.Text = $"下载完成: {data}";
}
// 在控制台应用程序中
public async Task ConsoleAppAsync()
{
Console.WriteLine($"开始在线程: {Thread.CurrentThread.ManagedThreadId}");
// 控制台应用没有特殊的同步上下文
await Task.Delay(1000);
// 可能在任何线程池线程上继续执行
Console.WriteLine($"继续在线程: {Thread.CurrentThread.ManagedThreadId}");
}
// 使用 ConfigureAwait 控制上下文流动
public async Task ConfigureAwaitExample()
{
// UI 线程
textBox.Text = "开始处理...";
// 不捕获上下文,可能在线程池线程继续
var data = await ProcessDataAsync().ConfigureAwait(false);
// 这里不在 UI 线程,需要手动调度
this.BeginInvoke(new Action(() => {
textBox.Text = $"处理完成: {data}";
}));
}
}
4、Async/Await 的完整使用
4.1 基础异步操作
class BasicAsyncOperations
{
// 1. 异步方法签名
public async Task SimpleAsyncMethod()
{
await Task.Delay(1000);
Console.WriteLine("异步操作完成");
}
// 2. 返回值的异步方法
public async Task<int> CalculateAsync(int input)
{
await Task.Delay(500);
return input * 2;
}
// 3. 异步 void 方法(谨慎使用,主要用于事件处理)
public async void ButtonClickHandler(object sender, EventArgs e)
{
try
{
await ProcessClickAsync();
}
catch (Exception ex)
{
// 必须处理异常,因为 async void 无法被调用者捕获
Console.WriteLine($"处理点击时出错: {ex.Message}");
}
}
// 4. 异步静态方法
public static async Task<string> GetStaticDataAsync()
{
await Task.Delay(200);
return "静态数据";
}
public async Task DemonstrateBasicUsage()
{
Console.WriteLine("=== 基础异步操作演示 ===\n");
// 1. 等待单个异步操作
Console.WriteLine("开始等待...");
await Task.Delay(1000);
Console.WriteLine("1秒后继续");
// 2. 获取异步结果
int result = await CalculateAsync(21);
Console.WriteLine($"计算结果: {result}");
// 3. 连续异步操作
string data = await FetchDataAsync();
int length = await ProcessDataAsync(data);
Console.WriteLine($"处理后的数据长度: {length}");
}
private async Task<string> FetchDataAsync()
{
await Task.Delay(300);
return "示例数据";
}
private async Task<int> ProcessDataAsync(string data)
{
await Task.Delay(200);
return data.Length;
}
private async Task ProcessClickAsync()
{
await Task.Delay(100);
}
}
4.2 异步异常处理
class AsyncExceptionHandling
{
public async Task DemonstrateExceptionHandling()
{
Console.WriteLine("=== 异步异常处理 ===\n");
// 1. 基本的异常处理
try
{
await FaultyAsyncMethod();
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"捕获异常: {ex.Message}");
}
// 2. 多个异步任务的异常处理
var task1 = FaultyAsyncMethod("任务1");
var task2 = FaultyAsyncMethod("任务2");
try
{
await Task.WhenAll(task1, task2);
}
catch (AggregateException ae)
{
Console.WriteLine($"聚合异常包含 {ae.InnerExceptions.Count} 个异常:");
foreach (var ex in ae.InnerExceptions)
{
Console.WriteLine($" - {ex.Message}");
}
}
// 3. 分别处理每个任务的异常
var task3 = FaultyAsyncMethod("任务3");
var task4 = FaultyAsyncMethod("任务4");
var allTasks = Task.WhenAll(task3, task4);
try
{
await allTasks;
}
catch
{
// 检查每个任务的异常状态
if (task3.IsFaulted)
Console.WriteLine($"任务3失败: {task3.Exception.InnerException.Message}");
if (task4.IsFaulted)
Console.WriteLine($"任务4失败: {task4.Exception.InnerException.Message}");
}
// 4. 超时处理
try
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
await LongRunningOperationAsync(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("操作超时被取消");
}
}
private async Task FaultyAsyncMethod(string name = "默认任务")
{
await Task.Delay(100);
throw new InvalidOperationException($"{name} 故意抛出异常");
}
private async Task LongRunningOperationAsync(CancellationToken cancellationToken)
{
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(1000, cancellationToken);
Console.WriteLine($"进行中... {i + 1}/10");
}
}
}
4.3 异步组合和并行
class AsyncComposition
{
public async Task DemonstrateComposition()
{
Console.WriteLine("=== 异步组合和并行 ===\n");
// 1. 顺序执行
Console.WriteLine("1. 顺序执行:");
var stopwatch = Stopwatch.StartNew();
var result1 = await ProcessStep1Async();
var result2 = await ProcessStep2Async(result1);
var finalResult = await ProcessStep3Async(result2);
Console.WriteLine($"顺序执行耗时: {stopwatch.ElapsedMilliseconds}ms");
Console.WriteLine($"最终结果: {finalResult}");
// 2. 并行执行
Console.WriteLine("\n2. 并行执行:");
stopwatch.Restart();
var taskA = ProcessStep1Async();
var taskB = ProcessStep2Async("预计算值");
var taskC = ProcessStep3Async(100);
var results = await Task.WhenAll(taskA, taskB, taskC);
Console.WriteLine($"并行执行耗时: {stopwatch.ElapsedMilliseconds}ms");
Console.WriteLine($"并行结果: [{string.Join(", ", results)}]");
// 3. 选择第一个完成的任务
Console.WriteLine("\n3. 选择第一个完成的任务:");
var fastTask = FastOperationAsync();
var slowTask = SlowOperationAsync();
var firstCompleted = await Task.WhenAny(fastTask, slowTask);
Console.WriteLine($"第一个完成的任务结果: {await firstCompleted}");
// 4. 带超时的任务选择
Console.WriteLine("\n4. 带超时的任务选择:");
var operationTask = SlowOperationAsync();
var timeoutTask = Task.Delay(1500);
var completedTask = await Task.WhenAny(operationTask, timeoutTask);
if (completedTask == operationTask)
{
Console.WriteLine($"操作完成: {await operationTask}");
}
else
{
Console.WriteLine("操作超时");
}
// 5. 异步 LINQ
Console.WriteLine("\n5. 异步 LINQ:");
var numbers = Enumerable.Range(1, 5);
var processedNumbers = await Task.WhenAll(numbers.Select(ProcessNumberAsync));
Console.WriteLine($"处理后的数字: [{string.Join(", ", processedNumbers)}]");
}
private async Task<string> ProcessStep1Async()
{
await Task.Delay(300);
return "步骤1结果";
}
private async Task<int> ProcessStep2Async(string input)
{
await Task.Delay(400);
return input.Length * 10;
}
private async Task<string> ProcessStep3Async(int input)
{
await Task.Delay(500);
return $"最终值: {input + 100}";
}
private async Task<string> FastOperationAsync()
{
await Task.Delay(200);
return "快速操作完成";
}
private async Task<string> SlowOperationAsync()
{
await Task.Delay(2000);
return "慢速操作完成";
}
private async Task<int> ProcessNumberAsync(int number)
{
await Task.Delay(100);
return number * number;
}
}
4.4 高级异步模式
class AdvancedAsyncPatterns
{
// 1. 异步工厂模式
public class AsyncResource : IAsyncDisposable
{
private AsyncResource() { }
public static async Task<AsyncResource> CreateAsync()
{
var resource = new AsyncResource();
await resource.InitializeAsync();
return resource;
}
private async Task InitializeAsync()
{
await Task.Delay(1000);
Console.WriteLine("资源初始化完成");
}
public async ValueTask DisposeAsync()
{
await CleanupAsync();
Console.WriteLine("资源清理完成");
}
private async Task CleanupAsync()
{
await Task.Delay(500);
}
}
// 2. 异步延迟初始化
private readonly AsyncLazy<ExpensiveResource> _expensiveResource =
new AsyncLazy<ExpensiveResource>(async () => {
var resource = new ExpensiveResource();
await resource.InitializeAsync();
return resource;
});
public async Task UseAsyncLazy()
{
var resource = await _expensiveResource;
await resource.DoSomethingAsync();
}
// 3. 异步锁模式
private readonly SemaphoreSlim _asyncLock = new SemaphoreSlim(1, 1);
public async Task<string> GetOrCreateAsync(string key)
{
// 快速路径:无锁检查
if (_cache.TryGetValue(key, out string value))
return value;
// 慢速路径:获取异步锁
await _asyncLock.WaitAsync();
try
{
// 双重检查
if (_cache.TryGetValue(key, out value))
return value;
// 创建新值
value = await CreateValueAsync(key);
_cache[key] = value;
return value;
}
finally
{
_asyncLock.Release();
}
}
// 4. 异步生产者-消费者
public async Task DemonstrateAsyncProducerConsumer()
{
var channel = Channel.CreateBounded<int>(10);
// 生产者
var producer = Task.Run(async () => {
for (int i = 0; i < 20; i++)
{
await channel.Writer.WriteAsync(i);
Console.WriteLine($"生产: {i}");
await Task.Delay(100);
}
channel.Writer.Complete();
});
// 消费者
var consumer = Task.Run(async () => {
await foreach (var item in channel.Reader.ReadAllAsync())
{
Console.WriteLine($"消费: {item}");
await Task.Delay(150);
}
});
await Task.WhenAll(producer, consumer);
}
// 5. 取消令牌支持
public async Task<int> LongRunningOperationWithCancellationAsync(
CancellationToken cancellationToken = default)
{
for (int i = 0; i < 100; i++)
{
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(100, cancellationToken);
// 模拟工作进度
if (i % 10 == 0)
Console.WriteLine($"进度: {i}%");
}
return 42;
}
// 6. 异步流 (C# 8.0+)
public async IAsyncEnumerable<int> GenerateAsyncSequence()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(200);
yield return i;
}
}
public async Task ConsumeAsyncStream()
{
await foreach (var number in GenerateAsyncSequence())
{
Console.WriteLine($"收到: {number}");
}
}
private Dictionary<string, string> _cache = new Dictionary<string, string>();
private async Task<string> CreateValueAsync(string key)
{
await Task.Delay(500);
return $"Value_for_{key}";
}
}
// 异步延迟初始化类
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<Task<T>> taskFactory) : base(taskFactory) { }
public TaskAwaiter<T> GetAwaiter() => Value.GetAwaiter();
}
public class ExpensiveResource
{
public Task InitializeAsync() => Task.Delay(1000);
public Task DoSomethingAsync() => Task.Delay(500);
}
4.5 性能优化技巧
class AsyncPerformanceOptimization
{
// 1. 使用 ValueTask 避免分配
private Dictionary<int, string> _cache = new Dictionary<int, string>();
public async ValueTask<string> GetCachedValueAsync(int key)
{
if (_cache.TryGetValue(key, out string value))
{
return value; // 同步返回,不分配 Task
}
return await FetchValueAsync(key); // 异步操作,分配 Task
}
// 2. 使用 ConfigureAwait(false)
public async Task<string> LibraryMethodAsync()
{
// 在库代码中,通常不需要回到原始上下文
var data = await FetchDataAsync().ConfigureAwait(false);
return ProcessData(data);
}
// 3. 避免异步包装同步方法
public Task<int> BadWrapperAsync()
{
// 错误:同步方法包装成异步
return Task.Run(() => ComputeIntensively()); // 不必要地使用线程池
}
public ValueTask<int> GoodWrapperAsync()
{
// 如果可能同步完成,使用 ValueTask
if (_cache.TryGetValue("result", out int cachedResult))
{
return new ValueTask<int>(cachedResult);
}
return new ValueTask<int>(ComputeAndCacheAsync());
}
// 4. 批量异步操作
public async Task<List<string>> ProcessBatchAsync(List<int> ids)
{
// 错误:逐个处理
// var results = new List<string>();
// foreach (var id in ids)
// {
// results.Add(await ProcessItemAsync(id));
// }
// 正确:批量处理
var tasks = ids.Select(id => ProcessItemAsync(id));
return (await Task.WhenAll(tasks)).ToList();
}
// 5. 异步方法的热路径优化
public async ValueTask<string> GetDataOptimizedAsync(int key)
{
// 热路径:快速检查
if (key < 0)
return "Invalid";
// 热路径:缓存检查
if (_cache.TryGetValue(key, out string value))
return value;
// 冷路径:异步获取
return await FetchFromSourceAsync(key);
}
public async Task DemonstratePerformance()
{
Console.WriteLine("=== 异步性能优化演示 ===\n");
// 测试 ValueTask 性能
var stopwatch = Stopwatch.StartNew();
for (int i = 0; i < 1000; i++)
{
_ = await GetCachedValueAsync(i % 10); // 大部分命中缓存
}
Console.WriteLine($"ValueTask 测试: {stopwatch.ElapsedMilliseconds}ms");
// 测试批量操作性能
var ids = Enumerable.Range(1, 100).ToList();
stopwatch.Restart();
var results1 = await ProcessBatchOneByOneAsync(ids);
Console.WriteLine($"逐个处理: {stopwatch.ElapsedMilliseconds}ms");
stopwatch.Restart();
var results2 = await ProcessBatchParallelAsync(ids);
Console.WriteLine($"批量处理: {stopwatch.ElapsedMilliseconds}ms");
}
private async Task<string> FetchValueAsync(int key)
{
await Task.Delay(10);
var value = $"Value_{key}";
_cache[key] = value;
return value;
}
private async Task<string> FetchDataAsync()
{
await Task.Delay(100);
return "data";
}
private string ProcessData(string data) => data.ToUpper();
private int ComputeIntensively()
{
Thread.Sleep(100);
return 42;
}
private async Task<int> ComputeAndCacheAsync()
{
await Task.Delay(100);
var result = 42;
_cache["result"] = result;
return result;
}
private async Task<string> ProcessItemAsync(int id)
{
await Task.Delay(10);
return $"Processed_{id}";
}
private async Task<string> FetchFromSourceAsync(int key)
{
await Task.Delay(100);
var value = $"Fetched_{key}";
_cache[key] = value;
return value;
}
private async Task<List<string>> ProcessBatchOneByOneAsync(List<int> ids)
{
var results = new List<string>();
foreach (var id in ids)
{
results.Add(await ProcessItemAsync(id));
}
return results;
}
private async Task<List<string>> ProcessBatchParallelAsync(List<int> ids)
{
var tasks = ids.Select(id => ProcessItemAsync(id));
return (await Task.WhenAll(tasks)).ToList();
}
}
5、Async/Await 的内部工作机制
5.1 状态机执行流程
class StateMachineExecution
{
// async/await 的完整执行流程:
public void AsyncMethodExecutionFlow()
{
// 1. 调用 async 方法时,同步执行直到第一个 await
// 2. 如果 await 的操作已经完成,继续同步执行
// 3. 如果 await 的操作未完成:
// a. 方法返回一个未完成的 Task
// b. 注册回调,当操作完成时恢复执行
// c. 让出当前线程,不阻塞
// 4. 当操作完成时,回调执行,状态机从 await 之后继续
// 5. 如果有关联的同步上下文,在原始上下文中恢复
}
// 状态机的生命周期
class AsyncStateMachineLifecycle
{
// 创建阶段:编译器生成状态机类
// 初始化阶段:设置初始状态和局部变量
// 执行阶段:MoveNext() 方法驱动状态流转
// 完成阶段:设置最终结果或异常
// 清理阶段:释放资源
}
}
5.2 线程使用分析
class ThreadUsageAnalysis
{
public async Task DemonstrateThreadUsage()
{
Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}");
// I/O 密集型操作 - 不使用线程等待
await File.ReadAllTextAsync("test.txt");
Console.WriteLine($"I/O 完成线程: {Thread.CurrentThread.ManagedThreadId}");
// CPU 密集型操作 - 使用线程池线程
await Task.Run(() => {
Console.WriteLine($"CPU 工作线程: {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
});
Console.WriteLine($"CPU 完成线程: {Thread.CurrentThread.ManagedThreadId}");
// Timer 回调 - 使用线程池线程
await Task.Delay(1000);
Console.WriteLine($"Timer 完成线程: {Thread.CurrentThread.ManagedThreadId}");
}
}
6、最佳实践和注意事项
6.1 正确模式
class AsyncBestPractices
{
// 1. 异步方法命名约定
public async Task<string> GetDataAsync() { ... } // 正确:Async 后缀
public async Task<string> GetData() { ... } // 避免:缺少 Async 后缀
// 2. 避免 async void(除了事件处理程序)
public async Task MethodAsync() { ... } // 正确
public async void BadMethod() { ... } // 避免
// 3. 合理使用 ConfigureAwait
public async Task<string> LibraryCodeAsync()
{
// 在库代码中通常使用 ConfigureAwait(false)
var data = await FetchDataAsync().ConfigureAwait(false);
return ProcessData(data);
}
public async Task UI CodeAsync()
{
// 在UI代码中通常需要回到UI线程
var data = await FetchDataAsync(); // 默认捕获上下文
UpdateUI(data);
}
// 4. 避免在异步方法中阻塞
public async Task<string> GoodPatternAsync()
{
return await GetDataAsync(); // 正确:使用 await
}
public string BadPattern()
{
return GetDataAsync().Result; // 错误:可能导致死锁
}
// 5. 正确处理取消
public async Task<int> ProcessWithCancellationAsync(
CancellationToken cancellationToken = default)
{
await Task.Delay(1000, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
return 42;
}
// 6. 异步初始化模式
private Task _initializationTask;
public Task EnsureInitializedAsync()
{
return _initializationTask ??= InitializeAsync();
}
private async Task InitializeAsync()
{
await Task.Delay(1000);
}
}
6.2 常见陷阱
class AsyncPitfalls
{
// 1. 死锁:在同步上下文中阻塞等待异步方法
public void DeadlockExample()
{
// 在 UI 线程中调用会导致死锁
// var result = GetDataAsync().Result;
// 解决方案:使用 async/await 贯穿始终
// 或者使用 ConfigureAwait(false)
}
// 2. 忘记 await
public async Task ForgetAwaitExample()
{
// 错误:忘记 await,异常可能被忽略
// ProcessDataAsync(); // 没有 await
// 正确:
await ProcessDataAsync();
}
// 3. 异常处理不当
public async void AsyncVoidException()
{
// async void 中的异常无法被调用者捕获
throw new InvalidOperationException("这会崩溃应用程序");
}
// 4. 过度并行化
public async Task OverParallelization()
{
// 错误:同时启动太多任务
// var tasks = Enumerable.Range(0, 10000).Select(_ => Task.Run(...));
// 正确:使用适当的并行度控制
var options = new ParallelOptions { MaxDegreeOfParallelism = 10 };
await Parallel.ForEachAsync(Enumerable.Range(0, 1000), options, async (i, ct) => {
await ProcessItemAsync(i);
});
}
// 5. 混合同步和异步代码
public async Task MixedSyncAsync()
{
// 错误:在异步方法中同步阻塞
// Thread.Sleep(1000);
// 正确:使用异步等待
await Task.Delay(1000);
}
private async Task ProcessDataAsync()
{
await Task.Delay(100);
}
private async Task ProcessItemAsync(int item)
{
await Task.Delay(10);
}
}
7、总结
Async/Await 的本质:
-
是基于状态机的语法糖,将异步回调转换为同步书写风格
-
使用续体传递风格(CPS) 实现非阻塞等待
-
依赖任务等待器模式 实现通用异步操作
核心优势:
-
代码简洁:像同步代码一样直观易读
-
非阻塞:在等待时不占用线程,提高资源利用率
-
可组合:轻松组合多个异步操作
-
异常安全:自然的异常处理机制
关键技术点:
-
async:标记方法为异步,启用编译器转换
-
await:等待异步操作完成,让出控制权
-
Task/Task<T>:表示异步操作
-
ValueTask:性能优化的轻量级任务
-
ConfigureAwait:控制同步上下文流动
-
CancellationToken:支持取消操作
使用要点:
-
异步方法命名以 Async 结尾
-
避免 async void(除事件处理程序外)
-
在库代码中考虑使用 ConfigureAwait(false)
-
避免在异步方法中阻塞
-
妥善处理异步异常

被折叠的 条评论
为什么被折叠?



