Task.Run()中的Aync / Await操作

Task.Run(()=>{})将操作委托放入队列并返回任务。 在Task.Run()中使用async / await有什么好处吗? 我知道Task.Run()是必需的,因为如果我们想直接使用await,那么调用方法将需要成为Async并且会影响调用的地方。

以下是在Task.Run()中具有异步等待的示例代码。 完整示例如下: https : //msdn.microsoft.com/en-us/library/hh228607(v = vs.110).aspx

 Task.Run(async () => { await new WebClient().DownloadStringTaskAsync("");}); 

或者这可以做到,

 Task.Run(() => new WebClient().DownloadStringTaskAsync("").Result;); 

由于Task.Run()和Await都将对工作进行排队并将被线程池选中,因此Task.Run()中的async / await是否有点多余?

在Task.Run()中使用async / await是否有任何好处

您也可以提出相反的问题:为什么要在Task.Run中包装async / await代码?!

一旦第一个await被命中(对未完成的任务进行操作),异步方法就会返回给调用者。 因此,如果异步方法的第一次执行“连胜”需要很长时间, Task.Run将改变行为:它将导致该方法立即返回并执行线程池上的第一个“条纹”。

这在UI场景中很有用,因为这样您可以100%确定您没有阻止UI。 示例:即使您使用其中一种异步方法, HttpWebRequest也会同步执行DNS解析(这基本上是库错误/设计错误)。 这可以暂停UI线程。 因此,您可以使用Task.Run 100%确保UI永远不会被阻止超过几微秒。

回到最初的问题:为什么要在Task.Run体内等待? 出于同样的原因,您通常会等待:取消阻止线程。

在Task.Run()中使用async / await有什么好处吗?

是。 Task.Run在线程池线程上运行一些操作。 如果此类操作执行某些IO工作并异步等待通过await完成IO操作,则在IO操作仍在运行时,系统可以使用此线程池线程进行其他工作。

例:

 Task.Run( async () => { DoSomeCPUIntensiveWork(); // While asynchronously waiting for this to complete, // the thread is given back to the thread-pool var io_result = await DoSomeIOOperation(); DoSomeOtherCPUIntensiveWork(io_result); }); 

在您链接的示例中,主线程被阻塞,直到异步操作完成。 通过调用Wait()来阻止它(顺便说一句,这通常是一个坏主意)。

让我们看一下链接样本中DownloadStringAsync的返回值:

 return Task.Run(async () => { content = await new WebClient().DownloadStringTaskAsync(address); cachedDownloads.TryAdd(address, content); return content; }); 

为什么要在Task包装它? 考虑一下您的选择。 如果你不想将它包装在一个Task ,你将如何确保该方法返回一个Task并仍然有效? 你当然把这个方法标记为async ! 但是,如果你将你的方法标记为async并且你在它上面调用Wait ,你很可能最终会出现死锁,因为主线程正在等待工作完成,并且你阻塞了主线程所以它不能让你知道它已经完成了。

将方法标记为async ,状态机将在调用线程上运行,但在您的示例中,状态机在单独的线程上运行,这意味着在主线程上几乎没有工作。

从非异步方法调用异步

当我们尝试在非异步方法中调用异步方法时,我们会做类似的事情。 特别是如果异步方法是已知数量。 我们使用了更多的TaskFactory,虽然…适合模式,使调试变得更容易,确保每个人都采用相同的方法(并且 – 如果异步 – >同步开始执行错误,则会让我们感到窒息)。

所以,你的例子

想象一下,在您的示例中,您具有非异步function。 并且,在该函数中,您需要调用await webClient.DoSomethingAsync() 。 你不能在一个非异步的函数内部调用await – 编译器不会让你。

选项1:僵尸感染

你的第一个选择是一直爬行你的调用堆栈,标记每个da * n方法作为异步并在任何地方添加等待。 到处。 喜欢,无处不在。 然后 – 因为所有这些方法现在都是异步的,所以你需要使引用它们的方法都是异步的。

顺便说一句,这可能是许多SO爱好者所倡导的方法。 因为,“阻止线程是一个坏主意”。

所以。 是啊。 这个“让异步为异步”的方法意味着您的小型库例程只需触及json对象并触及80%的代码。 谁会打电话给CTO让他知道?

选项2:与一个僵尸一起去吧

或者,您可以将async封装在像您这样的函数中…

 return Task.Run(async () => { content = await new WebClient().DoSomethingAsync(); cachedDownloads.TryAdd(address, content); return content; }); 

Presto … 僵尸感染已被包含在一段代码中。 我将把它留给比特机制来讨论如何/为什么在CPU级别执行。 我真的不在乎。 我担心没有人必须向CTO解释为什么整个库现在应该是100%异步(或类似的东西)。