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 inte易做图ce IAwaitable<out TResult>
{
IAwaiter<TResult> GetAwaiter();
}
public inte易做图ce 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 inte易做图ce IAwaitable<out TResult>
{
IAwaiter<TResult> GetAwaiter();
}
public inte易做图ce 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);//等待异步执行完成
}