C# 异步调用备忘
1.将一个同步方法封装为异步方法
a. 无返回值的封装
public async Task ReadAudioAsync(byte[] audio) { await Task.Run(() => PlaySound(audio, IntPtr.Zero, (uint)(SoundFlags.SND_MEMORY | SoundFlags.SND_SYNC))); }
b.有返回值的封装
public async Task<bool> ReadAudioAsync(byte[] audio) { var tcs = new TaskCompletionSource<bool>(); await Task.Run(() => { bool result = PlaySound(audio, IntPtr.Zero, (uint)(SoundFlags.SND_MEMORY | SoundFlags.SND_ASYNC)); tcs.SetResult(result); }); return await tcs.Task; }
如果调用一个返回值为 async Task 的方法,不想等待,也不想要返回结果,可以在方法前加 _ =,如下:
_ = ReadAudioAsync(new byte[]{});
2. 在同步方法中调用异步方法
a. 异步调用,不等待异步方法的完成。或者定义一个 async void 方法,调用时就像同步方法一样调用即可。
Task.Run(async () => { await ... await ... });
b. 同步调用,等待异步方法的完成
void Main() { //有返回值的等待 var task = GetDataAsync(); var data = task.Result; //无返回值的等待 DosthAsync().Wait(); } async Task<string> GetDataAsync() { await Task.Delay(1000); // 模拟异步操作 return "Hello, World!"; } async Task DosthAsync() { await Task.Delay(1000); // 模拟异步操作 }
3. 在异步方法返回一个常量
public async Task<bool> Download() { var source = new TaskCompletionSource<bool>(); source.SetResult(true); return await source.Task; }
4. 在异步方法中等待一定时长,不要使用 Theading.Sleep,使用如下代码
await Task.Delay(0);
5. 匿名异步等待
await new Func<Task>(async () => { await Task.Delay(1000); // 模拟异步操作 Console.WriteLine("异步操作完成"); })();
6. 强制某一个异步超时
//此处使用 HttpClient 异步下载,但无论如何设置 Timeout,都会有卡死的情况 public async Task DownloadFileTaskAsync(string fileUrl, string savePath) { // 发送 GET 请求 HttpResponseMessage response = await this.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead); // 确保请求成功 response.EnsureSuccessStatusCode(); // 获取文件流 using (var stream = await response.Content.ReadAsStreamAsync()) { // 创建文件流用于保存文件 using (var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None)) { // 将内容写入文件 await stream.CopyToAsync(fileStream); } } }
使用 Task.WhenAny 和 Task.Delay 实现任务超时,当然这样调用可能会产生一些临时的内存和CPU占用,但功能的可能性明显要高于性能损耗
public async Task<bool> DownloadFileTimeout(string fileUrl, string savePath) { var timeoutTask = Task.Delay(this.Timeout); // 调用下载文件的异步方法 var downloadTask = DownloadFileTaskAsync(fileUrl, savePath); // 等待第一个完成的任务(下载或超时) var completedTask = await Task.WhenAny(downloadTask, timeoutTask); if (completedTask == downloadTask) { return true; } else { return false; } }
7. 从一个内部匿名函数返回自定义类型(使用公共变量)
public async Task<IQueryable<TResult>> Select<TResult>(System.Linq.Expressions.Expression<Func<BookPage, TResult>> selector) { IQueryable<TResult> result = null; await SqlcipherConnectionLocal.RunInTransactionAsync(async (connection) => { var data = connection.Table<BookPage>().AsQueryable().Select(selector); result = await Task.FromResult(data); }); return result; }
8. 延迟等待异步结果(原本需要5秒,但延迟等待共耗时3秒)
async void Main() { //开始一个异步任务task,但并不立即等待结果 Task<int> task = MyTask(); //做自己的事情 await Task.Delay(2000); //等待task结果返回 int result = await task; Console.WriteLine($"{DateTime.Now}\tresult: {result}"); Console.ReadLine(); } public async Task<int> MyTask() { Console.WriteLine($"{DateTime.Now}\ttask started."); await Task.Delay(3000); Console.WriteLine($"{DateTime.Now}\ttask completed."); return 1; }
9. 创建一个双线执行的存取任务
Random random = new Random(); async void Main() { var tasks = CreateSequencialTasks(10); //朗读时需要按顺序获取创建的音频 foreach (var task in tasks) { string audio = await task; await ReadAudio(audio); } Console.WriteLine("all over."); Console.ReadLine(); } //创建一批顺序执行的任务 List<Task<string>> CreateSequencialTasks(int count) { var result = new List<Task<string>>(); Task<string> previousTask = Task.FromResult(string.Empty); for (int i = 0; i < count; i++) { int currentIndex = i; previousTask = CreateNextTask(previousTask, currentIndex); result.Add(previousTask); } return result; } Task<string> CreateNextTask(Task<string> antecedent, int index) { return antecedent.ContinueWith(async task => { string previousResult = await task; string nextResult = await CreateAudio(index); return nextResult; }).Unwrap(); } //朗读语音 public async Task ReadAudio(string audio) { Console.WriteLine($"{DateTime.Now} \t ReadAudio({audio}) started."); await Task.Delay(random.Next(2000, 5001)); Console.WriteLine($"{DateTime.Now} \t ReadAudio({audio}) completed."); } //生成语音 public async Task<string> CreateAudio(int index) { Console.WriteLine($"{DateTime.Now} \t CreateAudio({index}) started."); await Task.Delay(random.Next(500, 3001)); Console.WriteLine($"{DateTime.Now} \t CreateAudio({index}) completed."); return "Audio" + index; }
10. 永远可附加执行的队列
async Task Main() { var task1 = CreateAudio(1); await Task.Delay(5001); // 等待5秒钟 //即便task1已经结束了,也可以附加 ContinueWith 执行 var task2 = task1.ContinueWith(async previousTask => { return await CreateAudio(2); }).Unwrap(); var result1 = await task1; var result2 = await task2; Console.ReadLine(); } public async Task<string> CreateAudio(int index) { Console.WriteLine($"{DateTime.Now} \t CreateAudio({index}) started."); await Task.Delay(new Random().Next(500, 3001)); Console.WriteLine($"{DateTime.Now} \t CreateAudio({index}) completed."); return "Audio" + index; }
11. 多线程连续任务使用异步实现示例
Random random = new Random(); //音频生成任务链 Task<string> lastCreateTask = Task.FromResult(string.Empty); //音频朗读任务链 Task lastReadTask = Task.CompletedTask; void Main() { for (var i = 0; i < 10; i++) { addTask(i); } Console.ReadLine(); } void addTask(int input) { //新增音频生成任务到任务链 var createTask = lastCreateTask.ContinueWith(async previousTask => { return await CreateAudio(input); }).Unwrap(); lastCreateTask = createTask; //新增音频朗读任务到任务链 var readTask = lastReadTask.ContinueWith(async previousTask => { await ReadAudio(createTask); }).Unwrap(); lastReadTask = readTask; } //朗读语音 async Task ReadAudio(Task<string> task) { var audio = await task; Console.WriteLine($"{DateTime.Now} \t ReadAudio({audio}) started."); await Task.Delay(random.Next(2000, 5001)); Console.WriteLine($"{DateTime.Now} \t ReadAudio({audio}) completed."); } //生成语音 async Task<string> CreateAudio(int index) { Console.WriteLine($"{DateTime.Now} \t CreateAudio({index}) started."); await Task.Delay(new Random().Next(500, 3001)); Console.WriteLine($"{DateTime.Now} \t CreateAudio({index}) completed."); return "Audio" + index; }
桂棹兮兰桨,击空明兮溯流光。