1

In one of my methods, I want to create a car like this:

public string[] CreateCar()
{
    string[] wheels = CreateWheels();

    // Also add motor and other stuff here
    string[] motor = ...;

    return new string[][]{ wheels, motor, ... };
}

Here four wheels a created and they don't depend on each other. Hence, I can place them in multiple tasks, do be parallelised. (In my real code, this is not possible with a parallel-for loop)

public string[] CreateWheels()
{
    Task FL = Task.Run(() => CreateWheel("front left");
    Task FR = Task.Run(() => CreateWheel("front right");
    Task BL = Task.Run(() => CreateWheel("back left");
    Task BR = Task.Run(() => CreateWheel("back right");

    // Here I really want to wait for all wheels to be created!
    // STOP THE PROCESS HERE UNTIL ALL WHEELS ARE COMPLETED!!!
    string[] wheels = await Wask.WhenAll(FL, FR, BL, BR); 

    return wheels;
}

And one single wheel is created something like that:

public string CreateWheel(string position)
{
    // Here a wheel is created, whatever it takes :)
    return "wheel " + position;
}

My problem now is the following:
In order to get the code compiled, it forces me to mark CreateWheels as an async-Method. Otherwise I cannot use await.
⇒ But this forces me to change its return-value to be a Task<string[]>
⇒ However, then I need to put an await in front of CreateWheels() in the CreateCar-Method in order to get the string array and not the Task itself

⇒ Then the cycle repeats, since now, there is an await in the CreateWheels, forcing me to make it async ... and so on, and so on ...

This cycle repeats all the way up, until it finally comes to a void method, where you don't need to extract the return value with an await

What is the way out of this cycle, if I only want to wait for all wheels to be finished at the marked point?

8
  • Unless CreateCar is a particularity demanding method then I don't see why you want to use Task.Run to execute it. Commented Mar 27, 2020 at 14:04
  • 1
    string[] wheels = Task.WhenAll(FL, FR, BL, BR).Result; should work Commented Mar 27, 2020 at 14:05
  • @UnholySheep: oh thats nice! Do all tasks still get executed in parallel then? Commented Mar 27, 2020 at 14:06
  • 2
    Is there a reason you don't want to make the code async all the way down? Commented Mar 27, 2020 at 14:12
  • 1
    As others have mentioned async tends to be "infectious" in a way. Depending on how deeply nested this code is it can be kind of a pain to swap everything over, but its less of a pain than trying to debug any deadlock conditions you may end up with from using .Result on a task IMO. Commented Mar 27, 2020 at 14:23

3 Answers 3

2

If you want this to be synchronous, then Task.WaitAll may help, i.e.

Task.WaitAll(FL, FR, BL, BR);
var wheels = new[] {FL.Result, FR.Result, BL.Result, BR.Result};

If you want this to be asynchronous, you'll need to make the method async [Value]Task<string[]>, and propagate the async-ness throughout. Async is adhesive - it sticks to everything; but:

string[] wheels = await Task.WhenAll(FL, FR, BL, BR);

However, frankly, I suspect that there's no need for either in this case; parallelism isn't cheap - and is probably adding a lot more overhead than it is worth here.

Sign up to request clarification or add additional context in comments.

Comments

1

It is possible to achieve parallelism by starting multiple tasks with Task.Run, but why not use a specialized tool for the job like the PLINQ library? Not only you'll avoid using the Task.Result property, which is notorious for causing nasty deadlocks when combined with await, but you'll also get the ability to define the maximum degree of parallelism as a bonus:

var wheelNames = new[] { "front left", "front right", "back left", "back right" };
string[] wheels = wheelNames
    .AsParallel()
    .AsOrdered()
    .WithDegreeOfParallelism(4)
    .Select(x => CreateWheel(x))
    .ToArray();

Comments

0
string[] wheels = Wask.WhenAll(FL, FR, BL, BR).Result;

would do what you want. Those individual Tasks will still be run in parallel.

The downside is that your calling thread will block until these tasks are all resolved.

3 Comments

NIce thanks! That my calling thread is block cannot be avoided anyways, if the following code depends on the outcome of CreateWheels() right?
Instead of using WhenAll().Result why not use WaitAll() since it is the synchronous counterpart to WhenAll?
@TylerHundley Either is ok. WhenAll will unwrap all the tasks in one go and leave you with a normal array of strings. If you compare my answer to Marc's then you see that even after calling WaitAll he still has to deal with several Tasks and call Result several times. But it's just preference.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.