加入一年后
使用后异步等待了一年多了,我知道,关于异步我在原来的答案写了一些东西是不正确的,尽管答案中的代码仍然正确。赫拉是两个链接,帮助我极大地了解异步等待作品。
This interview Eric Lippert shows an excellent analogy for async-await。在中间的某处搜索异步等待。
In this article, the ever so helpful Eric Lippert shows some good practices for async-await
原来的答复
OK,这里是一个完整的例子,在学习过程中帮助了我。
假设你有一个慢速计算器,并且你想在按下按钮时使用它。同时你希望你的UI保持响应,甚至可以做其他事情。计算器完成后,您要显示结果。
当然:使用异步/等待这个,并没有任何设置事件标志和等待这些事件的旧方法设置。
这里是缓慢的计算器:
private int SlowAdd(int a, int b)
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a+b;
}
如果你想使用这个异步同时采用异步等待你必须使用Task.Run(...)以异步方式启动它。的Task.Run
返回值是一个awaitable任务:
Task
如果函数的返回值,你运行无效
Task<TResult>
如果您运行函数的返回值是TResult
你只需启动任务,执行其他操作,并且每当需要输入的任务结果等待时。有一个缺点:
如果你想“等待”你的函数必须是异步和返回Task
而不是void
或Task<TResult>
代替TResult
。
下面是运行慢速计算器的代码。 通常的做法是使用异步来终止异步函数的标识符。
private async Task<int> SlowAddAsync(int a, int b)
{
var myTask = Task.Run (() => SlowAdd(a, b));
// if desired do other things while the slow calculator is working
// whenever you have nothing to do anymore and need the answer use await
int result = await myTask;
return result;
}
边注:有些人喜欢Task.Factory.StartNew以上Start.Run。看看MSDN讲述了这一点:
MSDN: Task.Run versus Task.Factory.StartNew
的SlowAdd开始作为一个异步函数,你的线程继续。一旦它需要等待任务的答案。返回值是TResult,在本例中是一个int。
如果你有什么意义做的代码应该是这样的:谁使用这个异步函数也应该是异步和返回任务或任务
private async Task`<int`> SlowAddAsync(int a, int b)
{
return await Task.Run (() => SlowAdd(a, b));
}
注意SlowAddAsync声明异步功能,所以大家<TResult
>:
private async Task UpdateForm()
{
int x = this.textBox1.Text;
int y = this.textBox2.Text;
int sum = await this.SlowAddAsync(x, y);
this.label1.Text = sum.ToString();
}
约异步的好处/等待是,你不必与ContinueWith摆弄等到前面的任务就完成了。只需使用await,并且您知道任务已完成并且您具有返回值。等待之后的陈述是你在ContinueWith中通常要做的事情。
顺便说一句,你的Task.Run不必调用函数,你也可以把一个语句块中的:
int sum = await Task.Run(() => {
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a+b});
但是一个单独的函数的好处是,你给那些不需要/想要/理解异步的人,可以使用没有异步/等待的功能。
记住:
使用的await应该是异步
每个异步函数返回任务或任务<Tresult
每个函数>
“但我的事件处理程序不能返回任务!“
private void OnButton1_Clicked(object sender, ...){...}
你是对的,因此这是唯一的例外:
异步事件处理程序可能返回void
所以点击该按钮时,异步事件处理程序将继续UI响应:
private async void OnButton1_Clicked(object sender, ...)
{
await this.UpdateForm();
}
但是,您仍然必须声明t他的事件处理程序异步
许多.NET函数都有异步版本,它们返回任务或任务<TResult
>。
有用于 异步功能 - 上网 - 流的读取和写入 - 数据库访问 - 等
要使用他们,你不必叫Task.Run,他们已经返回任务和任务<TResult
>只需打电话给他们,继续做你自己的事情,当你需要答案等待任务和使用TResult。
启动多项任务,并等待他们完成 如果你开始的几个任务,要等到所有的人来完成,使用Task.WhenAll(...)不Task.Wait
Task.Wait返回一个void。 Task.WhenAll返回一个Task,所以你可以等待它。
一旦任务完成,返回值已经是await的返回值,但是如果您等待Task.WhenAll(new Task [] {TaskA,TaskB,TaskC}); 您必须使用任务<TResult
>。结果财产知道任务的结果:
int a = TaskA.Result;
如果任务之一抛出异常,它被包裹在一个AggregateException InnerExceptions。因此,如果您等待Task.WhenAll,请准备好捕获AggregateException并检查innerExceptions以查看您启动的任务抛出的所有异常。使用函数AggregateException.Flatten更容易地访问异常。
有趣的阅读关于取消:
MSDN about Cancellation in managed threads
最后:你使用了Thread.Sleep(...)。异步版本是Task.Delay(TimeSpan):
private async Task`<int`> MySlowAdd(int a, int b)
{
await Task.Delay(TimeSpan.FromSeconds(5));
return a+b;
}
如果您使用此函数,您的程序会保持响应。
异步/等待适用于方法,而不适用于任务。 –
@Henk'await'适用于具有合适的'GetAwaiter()'实现的表达式,其中'Task'确实 - 所以'await'适用于'Task'(某种意义上) –
在这种狭义的情况下,等待TaskEx.Delay(5000);',但你必须为你的一般情况研究更多。'await'关键字查找'GetAwaiter',基本上任何提供合适的'GetAwaiter'的东西都可以用于await。 Jon Skeet在他的博客中有一个很棒的系列,详细地等待着。 –