博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
async 和 await 关键字
阅读量:5998 次
发布时间:2019-06-20

本文共 3734 字,大约阅读时间需要 12 分钟。

C# 5.0中引入了async 和 await。这两个关键字可以让你更方便的按照同步的方式写出异步代码。也就是说使你更方便的异步编程。常规的写法格式如下:

 
  1. var result = await expression
  2. statement(s); 

这种写法相当于: 

 
  1. var awaiter = expression.GetAwaiter(); 
  2. awaiter.OnCompleted (() => 
  3. var result = awaiter.GetResult(); 
  4. statement(s); 
  5. ); 

这里的expression通常是Task或者Task<TResult>,但是事实上可以自己定义一些可以使用await的对象。但是要满足一定的条件。先看一个例子。 

 
  1. static void Main(string[] args) 
  2.        { 
  3.            DisplayPrimesCount(); 
  4.            Thread.Sleep(5000);//等待异步执行完成 
  5.        } 
  6.        static Task<int> GetPrimesCountAsync(int start, int count) 
  7.        { 
  8.            return Task.Run(() => 
  9.            ParallelEnumerable.Range(start, count).Count(n => 
  10.            Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0))); 
  11.        } 
  12.        static async void DisplayPrimesCount() 
  13.        { 
  14.            int result = await GetPrimesCountAsync(2, 1000000);//此处会阻塞 
  15.            Console.WriteLine(result); 
  16.        } 

这是比较常见的写法。

要异步执行GetPrimesCountAsync方法,首先GetPrimesCountAsync返回的必须是"可等待"对象。上述代码中GetPrimesCountAsync返回的是Task<int>类型。然后,使用await的方法必须要标注async关键字。并且可以看到await GetPrimesCountAsync(2, 1000000);这个语句返回的是int,而不是Task<int>。

上述代码用自然语言描述就是如下:

当调用DisplayPrimesCount,DisplayPrimesCount会运行一个新的Task,这个task会计算素数的个数。完成后,会将计算所得的值返回,并将这个返回值放到Task对象中,并且返回给调用者。调用者获得这个Task值后,取出Task的result值。

当程序逻辑遇到await GetPrimesCountAsync方法,线程就会被挂起,直到异步运行完成,得到result值后,再会继续运行下去。

本质上说await和async的出现也只是一颗语法糖,但是这颗语法糖可以使得异步编程更优雅,直接摒弃了原先EAP和APM这种到处BeginXXX,EndXXX的丑陋模式,提高了生产力。

可以使用await的方法,返回值必须是awaitable对象,自定义awaitable对象比较麻烦,一个对象必须满足下列条件才行:

  1. 必须有一个 GetAwaiter()方法,扩展方法或者实例方法都可以
  2. GetAwaiter() 方法返回值必须是awaiter对象。一个对象要成为awaiter对象必须满足下列条件:
    • 该对象实现接口 INotifyCompletion 或者ICriticalNotifyCompletion
    • 必须有 IsCompleted属性
    • 必须有 GetResult()方法,可以返回void或者其他返回值。

由于微软并未给出满足上述条件的接口,因此可以自己实现这样的接口。 

 
  1. public interface IAwaitable<out TResult> 
  2.     { 
  3.         IAwaiter<TResult> GetAwaiter(); 
  4.     } 
  5.     public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion 
  6.     { 
  7.         bool IsCompleted { get; } 
  8.         TResult GetResult(); 
  9.     } 

由于对于拉姆达表达式不可以直接使用await,因此可以通过编程,技巧性的实现这一功能。比如对某一个Func委托实现扩展方法,注意: 扩展方法必须在顶级静态类中定义。 

 
  1. public static class FuncExtensions 
  2.     { 
  3.         public static IAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function) 
  4.         { 
  5.             return new FuncAwaiter<TResult>(function); 
  6.         } 
  7.     } 
  8.     public interface IAwaitable<out TResult> 
  9.     { 
  10.         IAwaiter<TResult> GetAwaiter(); 
  11.     } 
  12.     public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion 
  13.     { 
  14.         bool IsCompleted { get; } 
  15.         TResult GetResult(); 
  16.     } 
  17.     internal struct FuncAwaitable<TResult> : IAwaitable<TResult> 
  18.     { 
  19.         private readonly Func<TResult> function; 
  20.         public FuncAwaitable(Func<TResult> function) 
  21.         { 
  22.             this.function = function; 
  23.         } 
  24.         public IAwaiter<TResult> GetAwaiter() 
  25.         { 
  26.             return new FuncAwaiter<TResult>(this.function); 
  27.         } 
  28.     } 
  29.     public struct FuncAwaiter<TResult> : IAwaiter<TResult> 
  30.     { 
  31.         private readonly Task<TResult> task; 
  32.         public FuncAwaiter(Func<TResult> function) 
  33.         { 
  34.             this.task = new Task<TResult>(function); 
  35.             this.task.Start(); 
  36.         } 
  37.         bool IAwaiter<TResult>.IsCompleted 
  38.         { 
  39.             get 
  40.             { 
  41.                 return this.task.IsCompleted; 
  42.             } 
  43.         } 
  44.         TResult IAwaiter<TResult>.GetResult() 
  45.         { 
  46.             return this.task.Result; 
  47.         } 
  48.         void INotifyCompletion.OnCompleted(Action continuation) 
  49.         { 
  50.             new Task(continuation).Start(); 
  51.         } 
  52.     } 
  53.      
  54. 在main中可以如下写: 
  55.  
  56.   static void Main(string[] args) 
  57.         { 
  58.             Func(() => { Console.WriteLine("await..");return 0;}); 
  59.             Thread.Sleep(5000);//等待异步执行完成 
  60.         } 
  61.         static async void Func(Func<int> f) 
  62.         { 
  63.             int result = await new Func<int>(f); 
  64.             Console.WriteLine(result); 
  65.         } 

其中:

Func方法可以异步执行了,因为Func<int>已经实现扩展方法GetAwaiter,并且返回值类型是自己定义的IAwaitable类型。

当然,更加简单的方法是,采用微软提供的Task对象,让拉姆达表达式返回Task类型就可以了。 

 
  1. static void Main(string[] args) 
  2.         { 
  3.             Func(() => 
  4.             { 
  5.                 return Task<int>.Run<int>(() => { return Enumerable.Range(1,100).Sum(); }); 
  6.             }); 
  7.             Thread.Sleep(5000);//等待异步执行完成 
  8.         } 
  9.         static async void Func(Func<Task<int>> f) 
  10.         { 
  11.             int result = await f(); 
  12.             Console.WriteLine(result); 
  13.         } 
---------------------------------

参考资料:《C# 5.0 IN A NUTSHELL》

本文转自cnn23711151CTO博客,原文链接: http://blog.51cto.com/cnn237111/1103029,如需转载请自行联系原作者

你可能感兴趣的文章
Cisco/H3C交换机配置与管理完全手册(第2版)卓越网正式到货
查看>>
让VMware ESX中的虚拟机随esx开机自动启动
查看>>
rhel6.5解决包的依赖的一个处理方法
查看>>
小功能隐藏着大学问---windows的ACL带来的挑战
查看>>
RSA2012系列(4):网络战揭秘
查看>>
Puppet扩展篇6-通过横向扩展puppetmaster增加架构的灵活性
查看>>
西安OpenParty11月29日活动高清图文回顾——新增西安APEC蓝美图!
查看>>
SFB 项目经验-16-呼叫前客户端性能测试
查看>>
我是如何帮助创业公司改进企业工作的
查看>>
taglist
查看>>
UITabBarController 的使用
查看>>
卡特兰数
查看>>
epoll实现机制分析
查看>>
windows 2008 r2 安装TabsStudio
查看>>
级联分类器训练
查看>>
linux下日语语言包安装
查看>>
Java内存回收 - 落日之心的日志 - 网易博客
查看>>
微软BI 之SSIS 系列 - 导出数据到 Excel 2013 的实现
查看>>
php 学习路线 赵兴壮2014年4月28 日 加油
查看>>
高精确度且线程分离的定时器——多媒体定时器
查看>>