I'm still having trouble wrapping my head around async/await. I'm trying to figure out if I need to wrap my CPU intensive workload in a Task.Run below. Can someone tell me which is the better way to do this?
Without Task.Run:
public async Task ProcessData()
{
//Get some data
var httpClient = new HttpClient();
var response = await httpClient.GetAsync("myUrl");
string data = await response.Content.ReadAsStringAsync();
//Calculate result
string result = null;
for (int i = 0; i < 10000000; i++)
{
//Do some CPU intensive work
}
//Save results
using (var fs = new FileStream("myFile.txt", FileMode.Create))
using (var writer = new StreamWriter(fs))
{
await writer.WriteLineAsync(result);
}
}
With Task.Run:
public async Task ProcessData()
{
//Get some data
var httpClient = new HttpClient();
var response = await httpClient.GetAsync("myUrl");
string data = await response.Content.ReadAsStringAsync();
//Calculate result
string result = null;
await Task.Run(() =>
{
for (int i = 0; i < 10000000; i++)
{
//Do some CPU intensive work
}
});
//Save results
using (var fs = new FileStream("myFile.txt", FileMode.Create))
using (var writer = new StreamWriter(fs))
{
await writer.WriteLineAsync(result);
}
}
My instinct is to not use the Task.Run but in that case would that mean that all code in an async method is going to be executed on a separate thread? Meaning the calling thread will not be interrupted until the very end (File stream is closed and method ends)?
Or will the caller thread be interrupted and go back to this method to do the CPU intensive part first and then go back to whatever it was doing while ProcessData() runs in the background for the FileStream part?
I hope that's clear.
Thanks in Advance
As describe in my async intro, an async method begins executing just like any other method, directly on the stack of the calling thread. When an await acts asynchronously, it will capture a "context", "pause" the method, and return to its caller. Later on, when the "asynchronous wait" (await) is complete, the method is resumed on that captured context.
So, this means:
would that mean that all code in an async method is going to be executed on a separate thread?
There is no separate thread. No thread at all "executes" the await because there's nothing to do. When the await completes, the method continues executing in that captured context. Sometimes this means the method is queued to a message queue, where it will eventually be run by the original calling thread (e.g., UI apps work this way). Other times this means the method is run by whatever thread pool thread is available (most other apps work this way).
Meaning the calling thread will not be interrupted until the very end (File stream is closed and method ends)? Or will the caller thread be interrupted and go back to this method to do the CPU intensive part first and then go back to whatever it was doing while ProcessData() runs in the background for the FileStream part?
The calling thread is never interrupted.
The CPU-bound portion of the work will run on whatever "context" was captured at the point of the await. This can be the original thread, or it can be a thread pool thread, depending on the context.
So, back to the original question:
Can someone tell me which is the better way to do this?
If this is code that can be called from different contexts, then I recommend not using Task.Run, but I do recommend documenting that the method does do CPU-bound work. That way, if a caller invokes this method from a UI thread, then it knows to use Task.Run when invoking it, to ensure the UI thread isn't blocked.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With