How do I get a return value from Task.WaitAll() in a console app?

C#.NetTask Parallel-LibraryConsole ApplicationAsync Await

C# Problem Overview


I am using a console app as a proof of concept and new need to get an async return value.

I figured out that I need to use Task.WaitAll() in my main method to avoid needing an async "main()" method, which is illegal.

I'm now stuck trying to figure out an overload that allows me to use generics or just returns an object that I can cast, but while in Main().

C# Solutions


Solution 1 - C#

You don't get a return value from Task.WaitAll. You only use it to wait for completion of multiple tasks and then get the return value from the tasks themselves.

var task1 = GetAsync(1);
var task2 = GetAsync(2);
Task.WaitAll(task1, task2);
var result1 = task1.Result;
var result2 = task2.Result;

If you only have a single Task, just use the Result property. It will return your value and block the calling thread if the task hasn't finished yet:

var task = GetAsync(3);
var result = task.Result;

It's generally not a good idea to synchronously wait (block) on an asynchronous task ("sync over async"), but I guess that's fine for a POC.

Solution 2 - C#

For best practice, use the new async way of doing things. Instead of

  • Task.WaitAll use await Task.WhenAll
  • Task.WaitAny use await Task.WhenAny

The code above can be written as:

var task1 = GetAsync(1);
var task2 = GetAsync(2);
var results = await Task.WhenAll(task1, task2);
var result1 = results[0];
var result2 = results[1];

Solution 3 - C#

There are different methods you can use depending on the context and your needs, but I tried out the answers from i3arnon and alltej, and in debugging I found that they both have the same problem... I ran both code samples in a console app I made. I set a break point in the code and found that on the line of execution with Task.WaitAll or Task.WhenAll. When I examined the tasks I found that they had already run - the task property Status was = ran to completion. In fact, I was able to completely remove the Task. line of code and still assign the variables values, so it was useless.

I found it was a very difficult task to truly make this kind of operation run as desired and also get the return values needed. Here is what I came up with:

Task[] taskArray = {
    Task.Factory.StartNew(() => ExecuteConcurrentTasks(1).Result),
    Task.Factory.StartNew(() => ExecuteConcurrentTasks(2).Result)
};
Task.WaitAll(taskArray);
var s = ((Task<string>)taskArray[0]).Result;

This is a simple example where a task array is created, then Task.WaitAll is used to execute the 2 tasks simultaneously, then in the last line I'm showing how to access the returned value from the first task.

Here is the code with some more context and more data:

private static async void MyAsyncMethod()
{
    Task[] taskArray = {
        Task.Factory.StartNew(() => ExecuteConcurrentTasks(1).Result),
        Task.Factory.StartNew(() => ExecuteConcurrentTasks(2).Result),
        Task.Factory.StartNew(() => ExecuteConcurrentTasks(3).Result),
        Task.Factory.StartNew(() => ExecuteConcurrentTasks(4).Result)
    };
    Task.WaitAll(taskArray);
    string s1 = ((Task<string>)taskArray[0]).Result;
    string s2 = ((Task<string>)taskArray[1]).Result;
    string s3 = ((Task<string>)taskArray[2]).Result;
    string s4 = ((Task<string>)taskArray[3]).Result;
}



private static async Task<string> ExecuteConcurrentTasks(int passedInt)
{
    string s = "Result: " + passedInt.ToString();

    if (passedInt == 4)
    {
        Console.WriteLine(s);
    }
    else if (passedInt == 3)
    {
        Console.WriteLine(s);
    }
    else if (passedInt == 2)
    {
        Console.WriteLine(s);
    }
    else if (passedInt == 1)
    {
        Console.WriteLine(s);
    }

    return s;
}

Here is the output:

output


UPDATE:

I got a bit more insight on these methods from working with some other developers. My understanding now is that .WaitAll will block execution in other parts of the program, which is an issue if you're not running just a very simple program. Also .WhenAll doesn't mean that the tasks won't execute until that line and all begin asynchronous execution at that line, it just means that the code in that block won't continue beyond that point unless all the specified tasks have finished running.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionmakerofthings7View Question on Stackoverflow
Solution 1 - C#i3arnonView Answer on Stackoverflow
Solution 2 - C#alltejView Answer on Stackoverflow
Solution 3 - C#SendETHToThisAddressView Answer on Stackoverflow