当前位置:编程学习 > C#/ASP.NET >>

async和await关键字

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 // or ICriticalNotifyCompletion 
    { 
        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 // or ICriticalNotifyCompletion 
    { 
        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
补充:软件开发 , C# ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,