Apeiron
Apeiron

Reputation: 602

Nicer pattern for starting tasks and getting their return values

I'm implementing some asynchronous work and cant help but feel I'm ending up with a really ugly construction and I'm wondering if and how this could be rewritten to something that 'feels better'.

 var tasks = new List<Task>();

 var t1 = new Task<Guid>(() => DoSomeStuff<Xyz>(dtx, e1, M1));
 var t2 = new Task<Guid>(() => DoSomeStuff<Qrs>(dtx, e2, M2));
 var t3 = new Task<Guid>(() => DoSomeStuff<Abc>(dtx, e3, M3));

 tasks.Add(t1);
 tasks.Add(t2);
 tasks.Add(t3);

 tasks.ForEach(x => x.Start());

 Task.WaitAll(tasks.ToArray<Task>());

 returnDto.t1value = t1.Result;
 returnDto.t2value = t2.Result;
 returnDto.t3value = t3.Result;

Variable names have been changed for brevity, there are actually a lot more tasks. Tasks can all run independently, but all have to be completed before we can continue.

DoSomeStuff looks like this:

private Guid DoSomeStuff<T>(DependentTransaction dtx, T thing, Func<T, Guid> method)

Upvotes: 3

Views: 162

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 456477

I'm implementing some asynchronous work

It's important to distinguish between asynchronous and parallel work. Asynchronous work is usually I/O-based, while parallel work uses multiple threads for CPU-bound code.

If your work is asynchronous, then there should be an asynchronous API you can use to make DoSomeStuff into an async method. Once that is done, it's relatively easy to do concurrent asynchronous work:

Guid[] results = await Task.WhenAll(DoSomeStuffAsync<Xyz>(dtx, e1, M1),
    DoSomeStuffAsync<Qrs>(dtx, e2, M2), DoSomeStuffAsync<Abc>(dtx, e3, M3));

However, if your work is parallel, then it's best to use the Parallel class (or Parallel LINQ):

Guid[] results = new Guid[3];
Parallel.Invoke(() => { results[0] = DoSomeStuff<Xyz>(dtx, e1, M1); },
    () => { results[1] = DoSomeStuff<Qrs>(dtx, e2, M2); },
    () => { results[2] = DoSomeStuff<Abc>(dtx, e3, M3); });

Upvotes: 1

i3arnon
i3arnon

Reputation: 116548

It would be much easier to use async-await to wait asynchronously, Task.WhenAll to wait for multiple tasks and get their results and Task.Run to run DoSomeStuff in parallel:

Guid[] results = await Task.WhenAll(
    Task.Run(() => DoSomeStuff<Xyz>(dtx, e1, M1)),
    Task.Run(() => DoSomeStuff<Qrs>(dtx, e2, M2)), 
    Task.Run(() => DoSomeStuff<Abc>(dtx, e3, M3)));

Upvotes: 5

Related Questions