昨天我开始玩Microsoft CTP异步库,无处找不到正在等待的任务的正确实现。我知道它必须有这样的实现吗?:
public struct SampleAwaiter<T>
{
private readonly Task<T> task;
public SampleAwaiter(Task<T> task) { this.task = task; }
public bool IsCompleted { get { return task.IsCompleted; } }
public void OnCompleted(Action continuation) { TaskEx.Run(continuation); }
public T GetResult() { return task.Result; }
}
但是我现在如何实现一个任务,比如等待5秒,然后返回一些字符串,例如“Hello World”?
一种方法是直接使用Task:
Task<string> task = TaskEx.Run(
() =>
{
Thread.Sleep(5000);
return "Hello World";
});
string str = await task;
但是如何通过等待实施来做到这一点?或者我只是误解了一切?
感谢您提供任何信息/帮助:)
答案 0 :(得分:16)
这里的关键是AsyncCtpThreadingExtensions.GetAwaiter
,它通过扩展方法提供这些方法。由于异步实现是基于模式(如LINQ),而不是绑定到特定接口,它可以来自任何地方(在这种情况下为TaskAwaiter
)。
您编写的代码是可以接受的。例如:
static void Main()
{
Test();
Console.ReadLine(); // so the exe doesn't burninate
}
static async void Test() {
Task<string> task = TaskEx.Run(
() =>
{
Thread.Sleep(5000);
return "Hello World";
});
string str = await task;
Console.WriteLine(str);
}
5秒后打印Hello World
。
答案 1 :(得分:2)
一年后加入
在使用async-await一年多之后,我知道我在原始答案中写的有关async的一些事情是不正确的,尽管答案中的代码仍然是正确的。 Hera是两个链接,帮助我非常了解async-await如何工作。
This interview Eric Lippert shows an excellent analogy for async-await。在中间某处搜索async-await。
In this article, the ever so helpful Eric Lippert shows some good practices for async-await
原始回答
好的,这是一个在学习过程中帮助我的完整示例。
假设您有一个慢速计算器,并且您想在按下按钮时使用它。同时,您希望您的UI保持响应,甚至可能做其他事情。计算器完成后,您想显示结果。
当然:对此使用async / await,并且没有设置事件标志和等待这些事件的旧方法。
这是慢速计算器:
private int SlowAdd(int a, int b)
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a+b;
}
如果要在使用async-await时异步使用它,则必须使用Task.Run(...)异步启动它。 Task.Run
的返回值是一个等待的任务:
Task
如果您运行的函数的返回值为void Task<TResult>
如果您运行的函数的返回值为TResult
您可以启动任务,执行其他操作,并且只要您需要任务的结果,您键入等待。有一个缺点:
如果你想等待&#39;您的函数需要异步并返回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;
}
旁注:有些人更喜欢Start.Run上方的Task.Factory.StartNew。了解MSDN对此的描述:
SlowAdd作为异步函数启动,您的线程继续。一旦它需要答案就等待任务。返回值是TResult,在本例中是一个int。
如果您没有任何意义,代码将如下所示:
private async Task`<int`> SlowAddAsync(int a, int b)
{
return await Task.Run ( () => SlowAdd(a, b));
}
请注意,SlowAddAsync被声明为异步函数,因此使用此异步函数的每个人也应该是异步并返回任务或任务<TResult
&gt;:
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();
}
async / await的好处在于你不必使用ContinueWith来等待上一个任务完成。只需使用await,您就知道任务已完成并且您具有返回值。等待之后的声明是您在ContinueWith中通常所做的事情。
顺便说一句,你的Task.Run不必调用一个函数,你也可以在其中放一个语句块:
int sum = await Task.Run( () => {
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return a+b});
然而,关于单独功能的好处是你给那些不需要/想要/理解异步的人,没有async / await就可以使用这个功能。
记住:
使用await的每个函数都应该是async
每个异步函数都应返回任务或任务
<Tresult
&gt;
&#34;但我的事件处理程序无法返回任务!&#34;
private void OnButton1_Clicked(object sender, ...){...}
你是对的,因此这是唯一的例外:
异步事件处理程序可能会返回void
因此,当单击按钮时,异步事件处理程序将保持UI响应:
private async void OnButton1_Clicked(object sender, ...)
{
await this.UpdateForm();
}
但是,您仍然需要声明事件处理程序async
许多.NET函数都有异步版本,可以返回任务或任务
<TResult
&gt;。
有异步功能 - 互联网 - 流读和写 - 数据库访问 - 等等。
要使用它们,您不必调用Task.Run,它们已经返回任务和任务<TResult
&gt;只需打电话给他们,继续做你自己的事情,当你需要答案等待任务并使用TResult。
启动多项任务并等待它们完成 如果您启动了几项任务并且想要等待所有任务完成,请使用Task.WhenAll(...) NOT Task.Wait
Task.Wait返回一个void。 Task.WhenAll返回一个Task,所以你可以等待它。
任务完成后,返回值已经是await的返回值,但是等待Task.WhenAll(new Task [] {TaskA,TaskB,TaskC});
您必须使用任务<TResult
&gt; .Result属性来了解任务的结果:
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;
}
如果您使用此功能,您的程序会保持响应。
答案 2 :(得分:1)
我最终得到了这个示例代码......这是正在等待的模式的正确实现吗?
namespace CTP_Testing
{
using System;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
public class CustomAsync
{
public static CustomAwaitable GetSiteHeadersAsync(string url)
{
return new CustomAwaitable(url);
}
}
public class CustomAwaitable
{
private readonly Task<string> task;
private readonly SynchronizationContext ctx;
public CustomAwaitable(string url)
{
ctx = SynchronizationContext.Current;
this.task = Task.Factory.StartNew(
() =>
{
var req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "HEAD";
var resp = (HttpWebResponse)req.GetResponse();
return this.FormatHeaders(resp.Headers);
});
}
public CustomAwaitable GetAwaiter() { return this; }
public bool IsCompleted { get { return task.IsCompleted; } }
public void OnCompleted(Action continuation)
{
task.ContinueWith(_ => ctx.Post(delegate { continuation(); }, null));
}
public string GetResult() { return task.Result; }
private string FormatHeaders(WebHeaderCollection headers)
{
var headerString = headers.Keys.Cast<string>().Select(
item => string.Format("{0}: {1}", item, headers[item]));
return string.Join(Environment.NewLine, headerString.ToArray());
}
}
}