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%异步(或类似的东西)。