.NET 异步编程模式 (四)-TAP

打印 上一主题 下一主题

主题 555|帖子 555|积分 1665

TAP 是基于任务的异步模式,在 .NET Framework 4 中引入。TAP取代了 APM 和EAP,是推荐的异步编程模式。
async / await

async 和 await 是为异步编程提供的语法糖,方便我们快捷编写异步代码。关键字 async 作用仅仅是为了能够使用 await 关键字以及怎么处理返回值。await 关键字可以想象成 asynchronous wait,在awaitable 完成之前,异步方法会等待,但线程不会堵塞。
  1. public async Task DoSomethingAsync()
  2. {
  3.     // For this example, we`re just going to (aynchronously) wait 100ms.
  4.     await Task.Delay(100);
  5. }
复制代码
对于调用方法,await 声明了一个挂起点,等异步方法结束后会捕获当前上下文继续执行后续代码。、
awaitable

await 就像是一元操作符,接收一个参数 - awaitable. Task 和 Task 都是这样的类型。
  1. public async Task NewStuffAsync()
  2. {
  3.     // Use await and have fun with the new stuff.
  4.     await ...
  5. }
  6. public Task MyOldTaskParallelLibraryCode()
  7. {
  8.     // Note that this is not an async method, so we can`t use await in here.
  9.     ...
  10. }
  11. public async Task ComposeAsync()
  12. {
  13.     // We can await Tasks, regardless of where they come from.
  14.     await NewStuffAsync();
  15.     await MyOldTaskParallelLibraryCode();
  16. }
复制代码
Task.Yield()

await Task.Yield() 方法来强制异步完成方法,可以让我们更好的控制异步方法的执行。如果当前任务很耗时,并且优先级比较低,可以考虑在方法开始的时候加上 await Task.Yield() ,让系统去调度其他更需要的任务,稍后再来完成该耗时任务。
  1. static async Task Process()
  2. {
  3.     await Task.Yield();
  4.     var tcs = new TaskCompletionSource<bool>();
  5.     Task.Run(() =>
  6.     {
  7.         Thread.Sleep(1000);
  8.         tcs.SetResult(true);
  9.     });
  10.     tcs.Task.Wait();
  11. }
复制代码
我不着急,我到后面从新排队去,你先去处理其他任务吧。其实是利用 await 实现线程的切换
Task.ConfigureAwait

默认情况,异步方法结束后会捕获和回复当前上下文。如果你不关系延续上下文,可以使用 Task.ConfigureAwait 指示不要回复而是继续执行等待的任务。
  1. await someTask.ConfigureAwait(continueOnCapturedContext:false);
复制代码
CancellationTokenSource

从 .NET Framework 4 开始,TAP 方法支持取消操作。
  1. var cts = new CancellationTokenSource();
  2. string result = await DownloadStringTaskAsync(url, cts.Token);
  3. … // at some point later, potentially on another thread
  4. cts.Cancel();
  5. // 取消多个异步调用
  6. var cts = new CancellationTokenSource();
  7. IList<string> results = await Task.WhenAll(from url in urls select DownloadStringTaskAsync(url, cts.Token));
  8. // at some point later, potentially on another thread
  9. cts.Cancel();
复制代码
Progress

通过 Progress 可以监控异步方法的执行进度。
  1. private async void btnDownload_Click(object sender, RoutedEventArgs e)
  2. {
  3.     btnDownload.IsEnabled = false;
  4.     try
  5.     {
  6.         txtResult.Text = await DownloadStringTaskAsync(txtUrl.Text,
  7.             new Progress<int>(p => pbDownloadProgress.Value = p));
  8.     }
  9.     finally { btnDownload.IsEnabled = true; }
  10. }
复制代码
Task.Run

Task.Run() 方法可以很方便的将耗时任务放到线程池上执行。
  1. public async void button1_Click(object sender, EventArgs e)
  2. {
  3.     // 默认恢复上下文
  4.     textBox1.Text = await Task.Run(() =>
  5.     {
  6.         // … do compute-bound work here
  7.         return answer;
  8.     });
  9. }
  10. public async void button1_Click(object sender, EventArgs e)
  11. {
  12.     // 内部使用 await
  13.     pictureBox1.Image = await Task.Run(async() =>
  14.     {
  15.         using(Bitmap bmp1 = await DownloadFirstImageAsync())
  16.         using(Bitmap bmp2 = await DownloadSecondImageAsync())
  17.         return Mashup(bmp1, bmp2);
  18.     });
  19. }
复制代码
Task.FromResult

Task.FromResult 用来创建一个带返回值的,已完成的 Task。
  1. public Task<int> GetValueAsync(string key)
  2. {
  3.     int cachedValue;
  4.     return TryGetCachedValue(out cachedValue) ?
  5.         Task.FromResult(cachedValue) :              // 如果本地有缓存,直接以同步的方式获取(但返回的是异步结果)
  6.         GetValueAsyncInternal();                    // 如果本地没有key对应的缓存,则异步从远端获取
  7. }
  8. // 异步方法从远端获取缓存
  9. private async Task<int> GetValueAsyncInternal(string key)
  10. {
  11.     …
  12. }
复制代码
Task.WhenAll

异步等待 一组异步操作的完成。
  1. Task [] asyncOps = (from addr in addrs select SendMailAsync(addr)).ToArray();
  2. try
  3. {
  4.     await Task.WhenAll(asyncOps);
  5. }
  6. catch(Exception exc)
  7. {
  8.     foreach(Task faulted in asyncOps.Where(t => t.IsFaulted))
  9.     {
  10.         … // work with faulted and faulted.Exception
  11.     }
  12. }
复制代码
Task.WhenAny

一组异步操作中,第一个异步操作完成时返回。

  • 可以同时进行多个相同的异步操作,选择最快完成的那个
  1. // 从多个行情源处获取行情,使用最快的那个
  2. var cts = new CancellationTokenSource();
  3. var recommendations = new List<Task<bool>>()
  4. {
  5.     GetBuyRecommendation1Async(symbol, cts.Token),
  6.     GetBuyRecommendation2Async(symbol, cts.Token),
  7.     GetBuyRecommendation3Async(symbol, cts.Token)
  8. };
  9. Task<bool> recommendation = await Task.WhenAny(recommendations);
  10. cts.Cancel(); // 取消剩余任务
  11. if (await recommendation) BuyStock(symbol);
复制代码

  • 多个任务交叉进行(每完成一个就处理一个)
  1. List<Task<Bitmap>> imageTasks =
  2.     (from imageUrl in urls select GetBitmapAsync(imageUrl)
  3.          .ContinueWith(t => ConvertImage(t.Result)).ToList();
  4. while(imageTasks.Count > 0)
  5. {
  6.     try
  7.     {
  8.         Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);
  9.         imageTasks.Remove(imageTask);
  10.         Bitmap image = await imageTask;
  11.         panel.AddImage(image);
  12.     }
  13.     catch{}
  14. }
复制代码
Task.Delay

在异步方法中暂定一段时间。 可以和 Task.WhenAny ,Task.WhenAll 结合使用以实现超时处理。
  1. public async void btnDownload_Click(object sender, EventArgs e)
  2. {
  3.     btnDownload.Enabled = false;
  4.     try
  5.     {
  6.         Task<Bitmap> download = GetBitmapAsync(url);
  7.         if (download == await Task.WhenAny(download, Task.Delay(3000)))
  8.         {
  9.             Bitmap bmp = await download;
  10.             pictureBox.Image = bmp;
  11.             status.Text = "Downloaded";
  12.         }
  13.         else
  14.         {
  15.             pictureBox.Image = null;
  16.             status.Text = "Timed out";
  17.             var ignored = download.ContinueWith(
  18.                 t => Trace("Task finally completed"));
  19.         }
  20.     }
  21.     finally { btnDownload.Enabled = true; }
  22. }
复制代码
公众号 - 希夏普


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

熊熊出没

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表