-1

After reading lot of articles and this forum itself i came to some conclusion i would like to confirm. Please of your answer. Let's assume our example async method presents as below. Please read also comments in code i placed. Let's assume that in this case both getStringTask and DoIndependentWork() takes quick therefore entire async method AccessTheWebAsync() would take quick job:

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;  
}

Then calls from diffrent places would look like:

//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
} 

Is this true that because our async method is taking quick job no matter from where it is called either UI/library/console app it is just need to use call like below means:

await getLengthTask ();  

Let's now consider same situation but consider either both of operations in async method would take long or just one of them:

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;  
}  

Does this change situation to this? :

//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 ();  
} 

In this case for UI related call Task.Run additionally has been added on await method because it's UI and we operate on long running async method. For rest console app and library project (or even for asp.net controller) signature still not changed.

Is my understanding correct? Summarizing it would mean only from UI related calls i need to add additional Task.Run so await Task.Run(asyncMethod) BUT ONLY IF if the async method as a whole could take a long time otherwise await would be just enough. But for rest library projects, console apps, asp.net controllers it's always just to use withotut Task.Run means await asynMethod() no matter either async method would take quick job or not.

DinoDin2
  • 121
  • 10
  • Possible duplicate of [Async/await and whyTask.Run has been used](https://stackoverflow.com/questions/53036767/async-await-and-whytask-run-has-been-used) – mjwills Oct 29 '18 at 09:05
  • 3
    To other commenters here, I spent over 2 hours commenting on the original question and back and forwarding with @DinoDin2. That insight, which directly answered this question, has now been deleted by the OP. **Keep that in mind when investing time into this question.** _The OP has also read http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html and http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-even-in.html which also answer this question._ – mjwills Oct 29 '18 at 09:10
  • 2
    Bear in mind that to `Task.Run` or not is more a question of *the nature of the work undertaken*, not time spent. If it's I/O bound and correctly written, you don't need to "help it out" by giving it another thread that it's not going to spend much time using anyway. – Damien_The_Unbeliever Oct 29 '18 at 09:11
  • 2
    This question could have been asked in about 3 paragraphs, the rest is just noise. Also Stephen Cleary sums it up well in those blogs – TheGeneral Oct 29 '18 at 09:20

1 Answers1

2

I am sure that @mjwills has answered this to death.

However lets put it simply. You only have 2 concerns :

  • Is this method going to going to block the thread
  • and do you care.

Since you have CPU and IO mixed operations, the first concern is answered.

Do you care? Well... if you do, and you are in any fear of blocking the UI thread or any other thread, your only sensible option is Wrap in it in a Task (and in some situations await it). If you don't care, then there is no need.

Its rare that you would want to mix IO and CPU. But yeah, we write libraries, and business layers, and this happens. If you are in a context where this is a problem, well there is not much more you can other than wrap it.

Update

IO is defined usually by anything that uses an IO completion port, I.e access a network or the file system, etc, etc.

CPU workload is usually defined, by anything that uses the CPU, calculation, Big for loops of logic. Anything that really isn't IO based.

Update

This question has been down every rabbit whole imaginable

When it all washes-out, the question asks should i wrap an async method which contains a blocking CPU workload in a task and wait for it.

CPU workloads block and will not relinquish the thread unless its fake async, i.e. it has internally wrapped the CPU workload in a task. This is less the optimal design and should be clearly documented.

However,

  • If you are in a server environment, there is no point creating a new thread/Task and waiting on it, you achieve no scalabilty points, taken an extra thread, and the caller still has to wait.

  • If you are in a UI thread, and you don't want to stall the UI, then yes its more appropriate to spin up a new task to complete this workload and await it (If you don't want to stall the message pump or message queue, meaning you don't want to stall the UI).

  • If you are in a console app, the awaited task (IO or CPU) will block no-matter-what. Do you care if it blocks? if you do, then run the method in a thread and give back control to the Console and don't await it, as it doesn't make any sense.

TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • Thanks but i think maybe the problem is i do not get what do you mean by saying "CPU and IO". You mean in the context of IO as input/output as for example file operations and CPU as some processor calculations or? What is the context used here for both? Does http request is CPU and for loop I/O? That what you mean? Please of clarification. Thanks. – DinoDin2 Oct 29 '18 at 09:47
  • General Thanks for your answer. What about database queries? Would it be I/O? And what if we have for instance mixed up two things let's say foor loop and some network access should i trade it as CPU bound when both mixed together? – DinoDin2 Oct 29 '18 at 10:18
  • ok and if we mix some I/O operation with CPU bound operation should method be consider as CPU bound then? – DinoDin2 Oct 29 '18 at 10:21
  • Ok we get to the main point now i get what did you mean by CPU bound and IO bound. Now if we take console app as example and have IO bound operations in the asyn method therefore call from Main should just use await AsynMethod but if method would contain CPU bound operations console app main would call it with await Task.Run or it will still be just await AsynMethod (because it's console but in case of UI this will be with await Task.Run())? – DinoDin2 Oct 29 '18 at 10:47
  • 1
    @DinoDin2 One thing here is to think about user expectations. With a UI (WPF, Winforms) the expectation is that the UI is responsive while work is being done. This is **not** the case for console apps or web apps. As such, `Task.Run` is generally not needed for console or web apps, even for CPU bound work. – mjwills Oct 29 '18 at 10:52
  • Ok my understanding at this point is that wrapping additional await by Task.Run() ( await Task.Run() ) is ONLY the case for UI (Forms/WPF) and ONLY when it comes to CPU bound operations or mixed (CPU Bound & IO Bound operations). Correct? – DinoDin2 Oct 29 '18 at 11:02
  • of course inside async method itself there shouldbn't be the Task.Run because method itself is implementation itself right? – DinoDin2 Oct 29 '18 at 11:11
  • 1
    `of course inside async method itself there shouldbn't be the Task.Run` What did Cleary's blog post say on this? – mjwills Oct 29 '18 at 11:12
  • @mjwills exactly same way because it would be in implementation itself means wrong way – DinoDin2 Oct 29 '18 at 11:12
  • @TheGeneral the last question, why then for instance in this topic https://stackoverflow.com/questions/37169579/async-await-or-task-run-in-console-application-windows-servic they used Task.Run in Main method, its console application in fact.. and we just talked that it's only valid in UI... to me they should mark Main as async (possible now) and make call as: await AsyncMethod() – DinoDin2 Oct 29 '18 at 11:27
  • @TheGeneral i undertood that but we just talked in couple earlier post that Task.Run should be used only in UI. So now it also becomes it should be also used in console app as well. – DinoDin2 Oct 29 '18 at 11:46
  • 1
    @DinoDin2 i understand your confusion, Console is a special case. it has no context, everything you do IO or CPU, async or not will block the same thread, unless you start a task or start it as a task. if you dont care to make the user wait, thats fine, if you care if the user cant enter data, you will need to start a task and do it in the background. Tasks are not usually what console apps do, however if you want to do stuff in the background you will need task, regardless of IO or CPU – TheGeneral Oct 29 '18 at 11:52
  • @TheGeneral Now i see so in this case for console and asyn methods it alsways have to make task.Run so the same as in UI. But why not with await like: var x = Task.Run(() => await MainAsync()); ? There is no await in link i posted – DinoDin2 Oct 29 '18 at 11:57
  • @TheGeneral what about if i would mark Main as async and then do: var first = Task.Run(() => MethodAsync0); var second = Task.Run(() => MethodAsync1); and then await Task.WaitAll(first, second); . To me that would make much more sense. – DinoDin2 Oct 29 '18 at 12:06
  • @TheGeneral i am talking always about parrael but await at some certain point so this would be fine approach to not block thread and await them? – DinoDin2 Oct 29 '18 at 12:08
  • 1
    @DinoDin2 updated.. however i really dont want to go down the path of every caveat and every situation. i think we are done with this question. – TheGeneral Oct 29 '18 at 23:17