Task.Run()与等待呼叫一起存在或不存在

时间:2018-10-29 09:01:32

标签: c#

在阅读了很多文章以及这个论坛本身之后,我得出了一些结论,我想确认一下。请回答。假设我们的示例async方法如下所示。请阅读我放置的代码中的注释。假设在这种情况下,getStringTaskDoIndependentWork()都需要快速执行,因此整个async方法AccessTheWebAsync()将需要快速完成:

async Task<int> AccessTheWebAsync()  
{    
   HttpClient client = new HttpClient();   
   Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com"); //assume it is quick job http request
   DoIndependentWork(); //assume it is quick job for loop
   string urlContents = await getStringTask;  
   return urlContents.Length;  
}

然后从不同地方拨打电话看起来像:

//example caller from some UI : either Froms or WPF
private async void StartButton_Click(object sender, RoutedEventArgs e)  
{   
   Task<int> getLengthTask = AccessTheWebAsync(); //it' call from UI but but still no need await Task.Run(AccessTheWebAsync) just await cause AccessTheWebAsync takes quick job
   SomeIndependentWorkMethodHere;.  
   int contentLength = await getLengthTask ();  
} 

//Call from Console application
private async static void Main()  
{   
   Task<int> getLengthTask = AccessTheWebAsync();           
   SomeIndependentWorkMethodHere;.  
   int contentLength = await getLengthTask (); //no need Task.Run() at all just await
} 

//Call from project library or aspnet controller
private static void MyMethod()  
{   
   Task<int> getLengthTask = AccessTheWebAsync();  
   SomeIndependentWorkMethodHere;.  
   int contentLength = await getLengthTask (); //no need Task.Run() at all just await
} 

是真的,因为我们的async方法无论在哪里被调用都可以快速完成工作,无论它是从UI / library / console应用程序中调用的,它都只需要使用如下所示的调用方式即可:

await getLengthTask ();  

现在让我们考虑相同的情况,但是考虑异步方法中的两个操作都将花费很长时间,或者只是其中之一:

async Task<int> AccessTheWebAsync()  
{    
   HttpClient client = new HttpClient();   
   Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com"); //assume it is long job http request
   DoIndependentWork();  //assume it is long job for loop
   string urlContents = await getStringTask;  
   return urlContents.Length;  
}  

这种情况会改变吗? :

//example caller from some UI : either Froms or WPF
private async void StartButton_Click(object sender, RoutedEventArgs e)  
{     
   SomeIndependentWorkMethodHere;.  
   await Task.Run(() => AccessTheWebAsync()); // <------ NOW additional Task.Run() is placed 
} 

//Call from Console application
private async static void Main()  
{   
   Task<int> getLengthTask = AccessTheWebAsync();  // <------- no need to change it's console even if async method is long (just await)
   SomeIndependentWorkMethodHere;.  
   int contentLength = await getLengthTask ();  
} 

//Call from project library or aspnet controller
private static void MyMethod()  
{   
   Task<int> getLengthTask = AccessTheWebAsync();  // <------- no need to change it's library project (or asp.net controller) even if async method is long (just await)
   SomeIndependentWorkMethodHere;.  
   int contentLength = await getLengthTask ();  
} 

在这种情况下,与UI相关的调用Task.Run已被添加到await方法中,因为它是UI并且我们在long running async method上进行操作。 对于其余console applibrary project(甚至对于asp.net controller),签名仍然没有更改。

我的理解正确吗?总结一下,这意味着仅从UI相关的调用中,我需要添加其他Task.Run,因此,如果await Task.Run(asyncMethod)方法作为一个整体可能需要很长时间,则async 但仅在时间 否则,等待就足够了。但是对于其余库项目,控制台应用程序,asp.net控制器,总是只使用withotut Task.Run意味着等待asynMethod(),无论异步方法是否需要快速工作。

1 个答案:

答案 0 :(得分:2)

我确信@mjwills已经回答了这个问题。

但是,简单地说。您只有两个问题:

  • 该方法是否要阻塞线程
  • 你在乎吗?

由于您具有CPU和IO混合操作,因此首先要解决的问题。

你在乎吗?好吧...如果这样做,并且您担心会阻塞UI线程或任何其他线程,则唯一明智的选择是在Task中的 Wrap 中(在某些情况下,情况等待着它)。如果您不在乎,则没有必要。

很少混合使用IO和CPU。但是,是的,我们编写了库和业务层,这确实发生了。如果您所处的环境是个问题,那么除了包装它之外,您将无能为力。

更新

IO通常是由使用IO完成端口,即访问网络或文件系统等的任何东西定义的。

CPU工作负载通常由使用CPU的任何东西,计算,逻辑循环的大定义。确实不是基于IO的任何内容。

更新

每个兔子都可以想到这个问题

全部洗完后,问题问我是否应该在任务中包装一个async方法,该方法包含阻塞的CPU工作负载,然后等待它。

CPU工作负载会阻塞并且不会放弃线程,除非其假async,即它已将CPU工作负载内部包装在一个任务中。这不是最佳设计,应该清楚地记录在案。

但是

  • 如果您在服务器环境中,创建新线程/任务并等待它是没有意义的,那么您就不会达到可扩展性点,需要额外的线程,并且调用者仍然必须等待。

  • p>
  • 如果您处于UI线程中,并且不想使UI停滞,那么,更合适的是启动一个新任务来完成此工作负载并等待它(如果您不想停止消息泵或消息队列,这意味着您不想停止UI)。

  • 如果您在控制台应用程序中,则等待的任务(IO或CPU)将阻止所有问题。你在乎它是否阻塞?如果这样做,则在线程中运行该方法,并将控制权交还控制台,不要等待它,因为这没有任何意义。