I know that Task.Run
is used for CPU Bound operations but I find it quite hard to understand when to use it in a scenario where you mix CPU and I/O bound operations. For example:
This is a little helper function which does some CPU work and also makes an http request:
public async Task<string> MakeHttpRequest(HttpRequestMessage request)
{
//before that do some CPU bound operations for example preparing the
// request before sending it out or validating the request object
var HttpClient = new HttpClient();
var response = await HttpClient.SendAsync(request);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
Now I need to make multiple calls with that method and I want to parallelize it so I get better performance out of my application. For that I generate a list of tasks with the method calls like this:
//Variant 1
public List<Task<string>> GenerateTasks()
{
HttpRequestMessage request = new HttpRequestMessage(); //...
List<Task<string>> taskList = new()
{
MakeHttpRequest(request),
MakeHttpRequest(request)
};
return taskList;
}
//Variant 2
public List<Task<string>> GenerateTasks2()
{
HttpRequestMessage request = new HttpRequestMessage(); //...
List<Task<string>> taskList = new()
{
Task.Run(() => MakeHttpRequest(request)),
Task.Run(() => MakeHttpRequest(request))
};
return taskList;
}
//Variant 3 - There is even this:
public List<Task<string>> GenerateTasks3()
{
HttpRequestMessage request = new HttpRequestMessage(); //...
List<Task<string>> taskList = new()
{
Task.Run(async () => await MakeHttpRequest(request)),
Task.Run(async () => await MakeHttpRequest(request))
};
return taskList;
}
At the end I would just do an await Task.WhenAll(GenerateTasks())
.
Pls note the lack of exception handling etc, it is just an example.
What is the difference between Variant 1,2 and 3? Since I am doing CPU Bound operations before the I/O operation would it be ok to use Task.Run
or not? Are there any negative side effects if it is not ok to use Task.Run
like this?