2

I have 2 long running methods that get data from external sources and return the results as lists:

public static class Helper1
{
    public static List<X> GetStuff()
    {
        // some long running stuff
    }
}

public static class Helper2
{
    public static List<Y> GetStuff()
    {
        // some long running stuff
    }
}

I would like to run them in parallel rather than sequentially. My first attempt is:

var task1 = new Task>(() => Helper1.GetStuff()); var task2 = new Task>(() => Helper2.GetStuff());

var whenAllTask = Task.WhenAll(task1, task2);

task1.Start();
task2.Start();

Nothing seems to happen and I wonder how I can access the content of the lists afterwards (program continues sequentially). Thanks!

PS:

I am now using the following code, given Robin B's answer:

 var task1 = new Task<List<X>>(() => Helper1.GetStuff(), TaskCreationOptions.LongRunning);
    var task2 = new Task<List<Y>>(() => Helper1.GetStuff(), TaskCreationOptions.LongRunning);

    var whenAllTask = Task.WhenAll(task1, task2).Wait();

    List<X> lst1 = task1.Result;
    List<Y> lst2 = task2.Result;

Unfortunately, nothing happens, It appears to be stuck.

PPS:

This seems to work for me:

var task1 = Task.Factory.StartNew<List<X>>(() => Helper1.GetStuff(), TaskCreationOptions.LongRunning));
var task2 = Task.Factory.StartNew<List<Y>>(() => Helper2.GetStuff(), TaskCreationOptions.LongRunning);

var allTasks = new Task[] { task1, task2 };

Task.WaitAll(allTasks);

List<X> lst1 = task1.Result;
List<Y> lst2 = task2.Result;
cs0815
  • 16,751
  • 45
  • 136
  • 299
  • 1
    `await whenAllTask;` to wait for the completion of `task1` and `task2` – Dmitry Bychenko Feb 11 '20 at 13:58
  • I would also advise to specify the `TaskCreationOptions.LongRunning` flag on creation, this will force the creation of a separate thread to run the task. –  Feb 11 '20 at 14:04
  • @Knoop thanks could you please provide example code? Thanks. – cs0815 Feb 11 '20 at 14:06
  • 2
    `t1` and `t2` are not defined. Are they related to this question? Also, the assignment for `task2` does not compile, since `Helper1.GetStuff` return a `List`. Please use real code for samples. – Rufus L Feb 11 '20 at 14:07
  • 1
    @cs0815 it's a flag you can pass to the `Task` constructor (or to `TaskFactory.StartNew` if you're using that). So an example would be `var task1 = new Task>(() => Helper1.GetStuff(), TaskCreationOptions.LongRunning);` –  Feb 11 '20 at 14:10
  • @RufusL sorry just spotted mistake and corrected – cs0815 Feb 11 '20 at 17:29
  • 1
    `var task2 = new Task>(() => Helper1.GetStuff());` - shouldn't this be `Helper2`? – Rufus L Feb 11 '20 at 20:56
  • @RufusL yes sorry corrected. – cs0815 Feb 11 '20 at 21:03

3 Answers3

3

WhenAll will wait for all given running tasks. So you will have to start them first. Also If you use Task<T> you can retrieve the result of the Task after it has finished by reading the Task<T>.Result property.

You will also notice that Task.WhenAll will return an awaitable. So you will have to call Wait() there or you will just fire and forget.

Here is an example:

  class Program
  {
    static void Main()
    {
      Task<List<int>> t1 = new Task<List<int>>(GetList);
      Task<List<int>> t2 = new Task<List<int>>(GetList);

      t1.Start();
      t2.Start();

      Task.WhenAll(t1, t2).Wait();

      Console.WriteLine("Both should have ended now");

      List<int> lst1 = t1.Result;
      List<int> lst2 = t2.Result;

      Console.ReadKey(true);
    }

    public static List<int> GetList()
    {
      Console.WriteLine("Started");

      Random rng = new Random();

      List<int> lst = new List<int>();

      for(int i = 0; i < 1000000; i++)
      {
        lst.Add(rng.Next());
      }

      Console.WriteLine("Ended");

      return lst;
    }
  }

EDIT You might want to read up on await vs Wait(). I did imply by you saying the program continues sequentially that you are in purely synchronous context and just need to improve fetching performance. But if you call this Code from a UI Thread or similar beware that the Wait() call will block your UI thread. In this case using await would be the better option. (await vs Task.Wait - Deadlock?)

Robin B
  • 1,066
  • 1
  • 14
  • 32
  • Thanks the code you provided works fine. However, it does not appear to work in my scenario, where I uses these static classes and methods. It just hangs. i put a break point into List GetStuff() and it is not hit. Any ideas? Thanks. – cs0815 Feb 11 '20 at 17:17
2

The cleaner way should be making your methods asynchronous so that they can be called asynchronously:

public static class Helper1
{
    public static Task<List<string>> GetStuffAsync()
    {
        return Task.Run(() => new List<string>{"78", "98", "56",});
    }
}

public static class Helper2
{
    public static Task<List<int>> GetStuffAsync()
    {
        return Task.Run(() => new List<int>{1, 2, 3, 4, 5, 6});
    }
}

Then call them like the following. The key here is the await keyword which tells the compiler that the tasks must be completed at this point.

static async Task Main(string[] args)
{
    var task1 = Helper1.GetStuff();
    var task2 = Helper2.GetStuff();
    
    await Task.WhenAll(task1, task2);

    Console.WriteLine(task1.Result);
    Console.WriteLine(task2.Result);
}
Md Hasan Ibrahim
  • 1,868
  • 19
  • 27
0

This seems to work for me:

var task1 = Task.Factory.StartNew<List<X>>(() => Helper1.GetStuff(), TaskCreationOptions.LongRunning));
var task2 = Task.Factory.StartNew<List<Y>>(() => Helper2.GetStuff(), TaskCreationOptions.LongRunning);

var allTasks = new Task[] { task1, task2 };

Task.WaitAll(allTasks);

List<X> lst1 = task1.Result;
List<Y> lst2 = task2.Result;
cs0815
  • 16,751
  • 45
  • 136
  • 299