Reputation: 53
i'm trying to make this code works in async way, but i got some doubts.
public async Task<string> GetAsync(string inputA, string inputB)
{
var result = await AnotherGetAsync(inputA, inputB)
.ConfigureAwait(false);
return result.Enabled
? inputB
: string.Empty;
}
I got some string input collection, and i would like to run this method on them. Then i would do Task.WhenAll, and filter for non empty strings.
But it won't be async as inside the method i'm already awaiting for the result right?
Upvotes: 5
Views: 119
Reputation: 43812
No, it will be async. When you await
a task inside an async method, the task will be awaited when the async method is invoked. Here is your example simplified, using explicit variables for the created tasks:
public async Task<string> GetAsync()
{
var innerTask = InnerGetAsync();
var result = await innerTask;
var stringResult = result.ToString();
return stringResult;
}
Later you create two tasks by invoking the GetAsync()
method:
var task1 = GetAsync();
var task2 = GetAsync();
At this point the two tasks are running concurrently. Each one has invoked internally the InnerGetAsync()
method, which means that the two innerTask
s are also up and running, and awaited. Any code that follows the await
will not run before the innerTask
is completed.
The completion of each outer task (task1 and task2) is dependent on the completion of its innerTask
. Any code that will await task1
, will also implicitly await innerTask
as well.
After creating the two tasks, you combine them with Task.WhenAll
, then do something else, then await
the combined task, and finally process the results:
Task<string[]> whenAllTask = Task.WhenAll(task1, task2);
DoSomethingElse();
string[] results = await whenAllTask;
ProcessResults(results);
It is important that the DoSomethingElse()
method will run before the two tasks are completed. So at this point three things will be happening concurrently, the DoSomethingElse()
and the two active tasks. The await
inside the GetAsync()
method is local to this method, and does not mean that the generated outer task will be automatically awaited upon creation. To process the results of task1
and task2
you must first await
them as well.
Upvotes: 2
Reputation: 5944
I assumed the real question is:
If a single item is awaited inside method A, will this run sequential if I use
Task.WhenAll
for a range of items calling method A?
They will be run simultaneous with Task.WhenAll
.
Perhaps it is best explained with an example:
void Main()
{
Test().GetAwaiter().GetResult();
}
private async Task<bool> CheckEmpty(string input)
{
await Task.Delay(200);
return String.IsNullOrEmpty(input);
}
private async Task Test()
{
var list = new List<string>
{
"1",
null,
"2",
""
};
var stopwatch = new Stopwatch();
stopwatch.Start();
// This takes aprox 4 * 200ms == 800ms to complete.
foreach (var itm in list)
{
Console.WriteLine(await CheckEmpty(itm));
}
Console.WriteLine(stopwatch.Elapsed);
Console.WriteLine();
stopwatch.Reset();
stopwatch.Start();
// This takes aprox 200ms to complete.
var tasks = list.Select(itm => CheckEmpty(itm));
var results = await Task.WhenAll(tasks); // Runs all at once.
Console.WriteLine(String.Join(Environment.NewLine, results));
Console.WriteLine(stopwatch.Elapsed);
}
My test results:
False
True
False
True
00:00:00.8006182
False
True
False
True
00:00:00.2017568
Upvotes: 2
Reputation: 20373
If we break this down:
AnotherGetAsync(inputA, inputB)
Returns a Task
. Using the await
keyword implicitly returns another Task
to the code calling GetAsync(string inputA, string inputB)
.
Using the await
keyword will free up the current thread, which is what makes the code asynchronous.
once the Task
returned by AnotherGetAsync(inputA, inputB)
is complete, result
will be set to that Task.Result
.
The remainder of the method will then resume:
return result.Enabled
? inputB
: string.Empty;
Setting the Task.Result
of the Task
returned from GetAsync
.
If GetAsync
is to be run multiple times, you can use Task.WhenAll
to wait for each resulting Task
to complete:
Task.WhenAll(yourStringCollection.Select(s => GetAsync(s, "another string"));
Task.WhenAll
itself returns another Task
that will complete when all the GetAsync
tasks have completed:
var strings = await Task.WhenAll(yourStringCollection.Select(s => GetAsync(s, "another string"));
var nonEmptyStrings = strings.Where(s => s != string.Empty);
Upvotes: 2