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是否有点多余?
答案 0 :(得分:6)
在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);
});
答案 1 :(得分:3)
在Task.Run()
中使用async / await是否有任何好处
您也可以提出相反的问题:为什么要在Task.Run中包装async / await代码?!
一旦第一个await被命中(对未完成的任务进行操作),异步方法就会返回给调用者。因此,如果异步方法的第一次执行“条带”花费很长时间Task.Run
将改变行为:它将导致该方法立即返回并执行线程池上的第一个“条纹”。
这在UI场景中非常有用,因为这样您可以100%确定您没有阻止UI。示例:即使您使用其中一种异步方法,HttpWebRequest
也会同步进行DNS解析(这基本上是库错误/设计错误)。这可以暂停UI线程。因此,您可以使用Task.Run 100%确保UI永远不会被阻止超过几微秒。
回到最初的问题:为什么要在Task.Run体内等待?出于同样的原因,您通常会等待:取消阻止该线程。
答案 2 :(得分:1)
在您链接的示例中,主线程被阻塞,直到异步操作完成。通过调用Wait()
来阻止它(顺便说一句,这通常是一个坏主意)。
让我们看一下linked示例中DownloadStringAsync
的回报:
return Task.Run(async () =>
{
content = await new WebClient().DownloadStringTaskAsync(address);
cachedDownloads.TryAdd(address, content);
return content;
});
为什么要将它包裹在Task
中?考虑一下您的选择。如果您不想将其包装在Task
中,您将如何确保该方法返回Task<string>
并仍然有效?当然,您将方法标记为async
!但是,如果您将方法标记为async
并且在其上调用Wait
,那么您很可能最终会遇到死锁,因为主线程正在等待工作完成,并且你阻止主线程,所以它不能让你知道它已经完成。
当将方法标记为async
时,状态机将在调用线程上运行,但在您的示例中,状态机在一个单独的线程上运行,这意味着在主体上几乎没有工作线程。
答案 3 :(得分:-1)
当我们尝试在非异步方法中调用异步方法时,我们做了类似的事情。特别是如果异步方法是已知数量。我们使用了更多的TaskFactory但是......适合模式,使其更容易调试,确保每个人都采用相同的方法(并且 - 如果异步 - &gt;同步开始执行错误,则让我们窒息)。< / p>
想象一下,在您的示例中,您具有非异步功能。并且,在该功能中,您需要调用等待webClient.DoSomethingAsync()。你不能在一个不是异步的函数中调用等待 - 编译器不会让你。
选项1:Zombie Infestation
你的第一个选择是一直爬行你的调用堆栈,标记每个da * n方法as async并在任何地方添加等待。到处。就像,无处不在。然后 - 因为所有这些方法现在都是异步的,所以你需要使引用它们的方法都是异步的。
顺便说一句,这可能是很多SO爱好者所倡导的方法。因为,&#34;阻止线程是一个坏主意。&#34;
因此。是啊。这个&#34;让异步为异步&#34;方法意味着您的小型库例程只需触及json对象并触及80%的代码。谁打算给CTO打电话让他知道?
选项2:与一个僵尸一起去
或者,你可以将async封装在像你这样的函数中......
return Task.Run(async () => {
content = await new WebClient().DoSomethingAsync();
cachedDownloads.TryAdd(address, content);
return content;
});
Presto ... zombie infestation 已包含在单个代码段中。我将它留给位机制来争论如何/为什么在CPU级别执行。我真的不在乎。我担心没有人必须向CTO解释为什么整个库现在应该是100%异步(或类似的东西)。
答案 4 :(得分:-3)
已确认,用 await
包装 Task.Run
使用 2 个线程而不是一个。
Task.Run(async () => { //thread #1
await new WebClient().DownloadStringTaskAsync(""); //thread #2
});
假设你有四个这样包装的调用,它将使用 4 x 2 = 8 个线程。
最好用简单的 await
来调用它们。例如:
Task<byte[]> t1 = new WebClient().DownloadStringTaskAsync("");
Task<byte[]> t2 = new WebClient().DownloadStringTaskAsync("");
byte[] t1Result = await t1;
byte[] t2Result = await t2;
这是包装 Task.Run
使用额外线程的证明。 (不使用WebClient
来证明这一点)
private static async Task wrapped()
{
List<Task> tasks = new List<Task>();
tasks.AddRange(new []
{
Task.Run(async() => await new MyThread().RunMe()),
Task.Run(async() => await new MyThread().RunMe()),
Task.Run(async() => await new MyThread().RunMe()),
Task.Run(async() => await new MyThread().RunMe()),
});
Thread.Sleep(1000);
int number = Process.GetCurrentProcess().Threads.Count;
Console.WriteLine($"While running thread count: {number}");
await Task.WhenAll(tasks);
}
未包装
private static async Task unwrapped()
{
List<Task> tasks = new List<Task>();
Task<int> t1 = new MyThread().RunMe();
Task<int> t2 = new MyThread().RunMe();
Task<int> t3 = new MyThread().RunMe();
Task<int> t4 = new MyThread().RunMe();
tasks.AddRange(new[] {t1, t2, t3, t4});
Thread.Sleep(1000);
int number = Process.GetCurrentProcess().Threads.Count;
Console.WriteLine($"While running thread count: {number}");
int i1 = await t1;
int i2 = await t2;
int i3 = await t3;
int i4 = await t4;
}
这里有完整的 POC 代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncThreadDemo
{
class Program
{
static async Task Main(string[] args)
{
int number = Process.GetCurrentProcess().Threads.Count;
Console.WriteLine($"Init thread count: {number}");
//await wrapped();
await unwrapped();
number = Process.GetCurrentProcess().Threads.Count;
Console.WriteLine($"Done thread count: {number}");
Console.ReadLine();
}
private static async Task wrapped()
{
List<Task> tasks = new List<Task>();
tasks.AddRange(new []
{
Task.Run(async() => await new MyThread().RunMe()),
Task.Run(async() => await new MyThread().RunMe()),
Task.Run(async() => await new MyThread().RunMe()),
Task.Run(async() => await new MyThread().RunMe()),
});
Thread.Sleep(1000);
int number = Process.GetCurrentProcess().Threads.Count;
Console.WriteLine($"While running thread count: {number}");
await Task.WhenAll(tasks);
}
private static async Task unwrapped()
{
List<Task> tasks = new List<Task>();
Task<int> t1 = new MyThread().RunMe();
Task<int> t2 = new MyThread().RunMe();
Task<int> t3 = new MyThread().RunMe();
Task<int> t4 = new MyThread().RunMe();
tasks.AddRange(new[] {t1, t2, t3, t4});
Thread.Sleep(1000);
int number = Process.GetCurrentProcess().Threads.Count;
Console.WriteLine($"While running thread count: {number}");
int i1 = await t1;
int i2 = await t2;
int i3 = await t3;
int i4 = await t4;
}
}
public class MyThread
{
public static int _counter;
public async Task<int> RunMe()
{
await Task.Run(() =>
{
for (int i = 0; i < 2; ++i)
{
Thread.Sleep(1000);
Console.WriteLine($"T{Thread.CurrentThread.ManagedThreadId} {i}");
}
Console.WriteLine($"T{Thread.CurrentThread.ManagedThreadId} done");
});
return _counter++;
}
}
}