visit
static async Task SimulateWorkAsync()
{
await Task.Delay(1000);
}
static async Task<long> SimulateLongWorkAsync()
{
var stopwatch = Stopwatch.StartNew();
await Task.Delay(5000);
return stopwatch.ElapsedMilliseconds;
}
static async Task<HttpStatusCode> CheckHttpStatusAsync(string url)
{
var response = await httpClient.GetAsync(url);\
return response.StatusCode;
}
await Task.WhenAll(
SimulateWorkAsync(),
SimulateLongWorkAsync(),
CheckHttpStatusAsync("//www.google.com"),
CheckHttpStatusAsync("//www.facebook.com"),
CheckHttpStatusAsync("//slowwly.robertomurray.co.uk/delay/3000/url///www.microsoft.com")
);
Side note: As seen used above, I’ve found to be a great website to simulate slow HTTP requests. It allows you to specify a delayed response directly in the URL.
While this does accomplish our goal of running the methods in parallel, what if we need to use the return value of these methods? One common way of achieving this is by keeping each
Task
in it’s own variable, so that the .Result
can be accessed later:var workTask = SimulateWorkAsync();
var longWorkTask = SimulateLongWorkAsync();
var googleTask = CheckHttpStatusAsync("//www.google.com");
var facebookTask = CheckHttpStatusAsync("//www.facebook.com");
var microsoftTask = CheckHttpStatusAsync("//slowwly.robertomurray.co.uk/delay/3000/url///www.microsoft.com");
await Task.WhenAll(
workTask,
longWorkTask,
googleTask,
facebookTask,
microsoftTask
);
await Task.WhenAll(
SimulateWorkAsync(),
SimulateLongWorkAsync().ContinueWith(cont =>
Console.WriteLine($"{nameof(SimulateLongWorkAsync)} took {cont.Result} ms.")),
CheckHttpStatusAsync("//www.google.com").ContinueWith(cont =>
Console.WriteLine($"//www.google.com returned {cont.Result}")),
CheckHttpStatusAsync("//www.facebook.com").ContinueWith(cont =>
Console.WriteLine($"//www.facebook.com returned {cont.Result}")),
CheckHttpStatusAsync("//slowwly.robertomurray.co.uk/delay/3000/url///www.microsoft.com").ContinueWith(cont =>
Console.WriteLine($"//slowwly.robertomurray.co.uk/delay/3000/url///www.microsoft.com returned {cont.Result}"))
);
With this, we can see that the calls to
CheckHttpStatusAsync
are essentially the same (aside from the URL that’s passed in). We should be able to consolidate this even further by using LINQ to execute that method against a collection of URLs:var urls = new[]
{
"//www.google.com",
"//www.facebook.com",
"//slowwly.robertomurray.co.uk/delay/3000/url///www.microsoft.com"
};
await Task.WhenAll(
SimulateWorkAsync(),
SimulateLongWorkAsync().ContinueWith(cont =>
Console.WriteLine($"{nameof(SimulateLongWorkAsync)} took {cont.Result} ms.")),
urls.Select(url => CheckHttpStatusAsync(url).ContinueWith(cont =>
Console.WriteLine($"{url} returned {cont.Result}")))
);
However, this doesn’t work since we’re now passing different types (
Task
and IEnumerable<Task>
) into Task.WhenAll
. We can solve this problem by using the Enumerable
static methods to build a single IEnumerable<Task>
that we can pass into Task.WhenAll
:var urls = new[]
{
"//www.google.com",
"//www.facebook.com",
"//slowwly.robertomurray.co.uk/delay/3000/url///www.microsoft.com"
};
await Task.WhenAll(Enumerable.Empty<Task>()
.Append(SimulateWorkAsync())
.Append(SimulateLongWorkAsync().ContinueWith(cont =>
Console.WriteLine($"{nameof(SimulateLongWorkAsync)} took {cont.Result} ms.")))
.Concat(urls.Select(url => CheckHttpStatusAsync(url).ContinueWith(cont =>
Console.WriteLine($"{url} returned {cont.Result}"))))
);