简体   繁体   中英

Return "normal" variable from async Task Method

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?

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

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.

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();
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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM