C# 5.0中引入了async 和 await。这两个关键字可以让你更方便的按照同步的方式写出异步代码。也就是说使你更方便的异步编程。常规的写法格式如下:
- var result = await expression;
- statement(s);
这种写法相当于:
- var awaiter = expression.GetAwaiter();
- awaiter.OnCompleted (() =>
- {
- var result = awaiter.GetResult();
- statement(s);
- );
这里的expression通常是Task或者Task<TResult>,但是事实上可以自己定义一些可以使用await的对象。但是要满足一定的条件。先看一个例子。
- static void Main(string[] args)
- {
- DisplayPrimesCount();
- Thread.Sleep(5000);
- }
- static Task<int> GetPrimesCountAsync(int start, int count)
- {
- return Task.Run(() =>
- ParallelEnumerable.Range(start, count).Count(n =>
- Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
- }
- static async void DisplayPrimesCount()
- {
- int result = await GetPrimesCountAsync(2, 1000000);
- Console.WriteLine(result);
- }
这是比较常见的写法。
要异步执行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对象比较麻烦,一个对象必须满足下列条件才行:
- 必须有一个 GetAwaiter()方法,扩展方法或者实例方法都可以
- GetAwaiter() 方法返回值必须是awaiter对象。一个对象要成为awaiter对象必须满足下列条件:
- 该对象实现接口 INotifyCompletion 或者ICriticalNotifyCompletion
- 必须有 IsCompleted属性
- 必须有 GetResult()方法,可以返回void或者其他返回值。
由于微软并未给出满足上述条件的接口,因此可以自己实现这样的接口。
- public interface IAwaitable<out TResult>
- {
- IAwaiter<TResult> GetAwaiter();
- }
- public interface IAwaiter<out TResult> : INotifyCompletion
- {
- bool IsCompleted { get; }
- TResult GetResult();
- }
由于对于拉姆达表达式不可以直接使用await,因此可以通过编程,技巧性的实现这一功能。比如对某一个Func委托实现扩展方法,注意: 扩展方法必须在顶级静态类中定义。
- public static class FuncExtensions
- {
- public static IAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function)
- {
- return new FuncAwaiter<TResult>(function);
- }
- }
- public interface IAwaitable<out TResult>
- {
- IAwaiter<TResult> GetAwaiter();
- }
- public interface IAwaiter<out TResult> : INotifyCompletion
- {
- bool IsCompleted { get; }
- TResult GetResult();
- }
- internal struct FuncAwaitable<TResult> : IAwaitable<TResult>
- {
- private readonly Func<TResult> function;
- public FuncAwaitable(Func<TResult> function)
- {
- this.function = function;
- }
- public IAwaiter<TResult> GetAwaiter()
- {
- return new FuncAwaiter<TResult>(this.function);
- }
- }
- public struct FuncAwaiter<TResult> : IAwaiter<TResult>
- {
- private readonly Task<TResult> task;
- public FuncAwaiter(Func<TResult> function)
- {
- this.task = new Task<TResult>(function);
- this.task.Start();
- }
- bool IAwaiter<TResult>.IsCompleted
- {
- get
- {
- return this.task.IsCompleted;
- }
- }
- TResult IAwaiter<TResult>.GetResult()
- {
- return this.task.Result;
- }
- void INotifyCompletion.OnCompleted(Action continuation)
- {
- new Task(continuation).Start();
- }
- }
-
- 在main中可以如下写:
-
- static void Main(string[] args)
- {
- Func(() => { Console.WriteLine("await..");return 0;});
- Thread.Sleep(5000);
- }
- static async void Func(Func<int> f)
- {
- int result = await new Func<int>(f);
- Console.WriteLine(result);
- }
其中:
Func方法可以异步执行了,因为Func<int>已经实现扩展方法GetAwaiter,并且返回值类型是自己定义的IAwaitable类型。
当然,更加简单的方法是,采用微软提供的Task对象,让拉姆达表达式返回Task类型就可以了。
- static void Main(string[] args)
- {
- Func(() =>
- {
- return Task<int>.Run<int>(() => { return Enumerable.Range(1,100).Sum(); });
- });
- Thread.Sleep(5000);
- }
- static async void Func(Func<Task<int>> f)
- {
- int result = await f();
- Console.WriteLine(result);
- }
---------------------------------
参考资料:《C# 5.0 IN A NUTSHELL》
本文转自cnn23711151CTO博客,原文链接: http://blog.51cto.com/cnn237111/1103029,如需转载请自行联系原作者