async 和 await 关键字
2012-12-28 14:54
260 查看
C# 5.0中引入了async 和 await。这两个关键字可以让你更方便的按照同步的方式写出异步代码。也就是说使你更方便的异步编程。常规的写法格式如下:
这种写法相当于:
这里的expression通常是Task或者Task<TResult>,但是事实上可以自己定义一些可以使用await的对象。但是要满足一定的条件。先看一个例子。
这是比较常见的写法。
要异步执行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或者其他返回值。
由于微软并未给出满足上述条件的接口,因此可以自己实现这样的接口。
由于对于拉姆达表达式不可以直接使用await,因此可以通过编程,技巧性的实现这一功能。比如对某一个Func委托实现扩展方法,注意: 扩展方法必须在顶级静态类中定义。
其中:
Func方法可以异步执行了,因为Func<int>已经实现扩展方法GetAwaiter,并且返回值类型是自己定义的IAwaitable类型。
当然,更加简单的方法是,采用微软提供的Task对象,让拉姆达表达式返回Task类型就可以了。
参考资料:《C# 5.0 IN A NUTSHELL》
http://weblogs.asp.net/dixin/archive/2012/11/08/understanding-c-async-await-2-awaitable-awaiter-pattern.aspx
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 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》
http://weblogs.asp.net/dixin/archive/2012/11/08/understanding-c-async-await-2-awaitable-awaiter-pattern.aspx
相关文章推荐
- Async和await关键字的用法
- Windows 8实用窍门系列:5.Windows 8弹出提示框MessageDialog与await、async关键字
- 一个async和await 关键字的简单入门
- 可移植类库无法使用async、await关键字
- Async 与 Await 关键字研究
- Visual Studio Async CTP的实现原理浅析 - 如何不使用async和await关键字来实现Async
- .NET async await 关键字最简单例子
- 解决nodejs不支持async和await关键字的问题
- .NET 4.5新关键字async和await:
- .NET中的async和await关键字使用及Task异步调用实例
- 浅谈async、await关键字 => 深谈async、await关键字
- C# 5.0 两个新关键字 async await 在Silverlight中的应用
- 《C#并发编程经典实例》学习笔记—异步编程关键字 Async和Await
- 为什么我们要使用Async、Await关键字
- async修饰符,await运算符关键字(C# 参考)
- async和await关键字实现异步编程
- C# 异步调用之async及await关键字
- .NET/C# 使用async和await关键字调用异步方法
- .NET async await 关键字最简单例子
- 【转】.NET 4.5 使用async和await关键字调用异步方法