Возврат "нормальной" переменной из asyn c Task Method - PullRequest
1 голос
/ 27 марта 2020

Одним из моих методов я хочу создать автомобиль, подобный этому:

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

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

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

Здесь четыре колеса созданы, и они не зависят друг от друга. Следовательно, я могу разместить их в нескольких tasks, распараллеливаться. (В моем реальном коде это невозможно с помощью параллели - для l oop)

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;
}

И одно колесо создается примерно так:

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

Моя проблема сейчас заключается в следующем:
Чтобы скомпилировать код, он заставляет меня пометить CreateWheels как async -метод. В противном случае я не могу использовать await.
⇒ Но это заставляет меня изменить его возвращаемое значение на Task<string[]>
⇒ Однако тогда мне нужно поставить await перед CreateWheels() в CreateCar -метод для получения массива строк, а не самой Задачи

⇒ Затем цикл повторяется, так как теперь в CreateWheels есть await, заставляя меня сделать это async ... и так далее, и так далее ...

Этот цикл повторяется до самого конца, пока, наконец, не дойдет до метода void, где вам не нужно извлекать вернуть значение с await

Какой выход из этого цикла, если я только хочу дождаться окончания всех колес в отмеченной точке?

Ответы [ 3 ]

2 голосов
/ 27 марта 2020

Если вы хотите, чтобы это было синхронно, тогда может помочь Task.WaitAll, то есть

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

Если вы хотите, чтобы это было асинхронным, вам нужно сделать метод async [Value]Task<string[]> и распространять Асин c -несс всюду. Асин c клей - он прилипает ко всему; но:

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

Однако, честно говоря, я подозреваю, что в этом нет необходимости; параллелизм недешев - и, вероятно, добавляет лот больше накладных расходов, чем здесь стоит.

1 голос
/ 27 марта 2020

Можно достичь параллелизма, запустив несколько задач с Task.Run, но почему бы не использовать специализированный инструмент для работы, такой как библиотека PLINQ ? Вы не только избежите использования свойства Task.Result, которое известно как вызывающее неприятные тупики в сочетании с await, но также получите возможность определить максимальную степень параллелизма как бонус:

var wheelNames = new[] { "front left", "front right", "back left", "back right" };
string[] wheels = wheelNames
    .AsParallel()
    .WithDegreeOfParallelism(4)
    .Select(x => CreateWheel(x))
    .ToArray();
0 голосов
/ 27 марта 2020
string[] wheels = Wask.WhenAll(FL, FR, BL, BR).Result;

будет делать то, что вы хотите. Эти отдельные задачи будут по-прежнему выполняться параллельно.

Недостатком является то, что ваш вызывающий поток будет блокироваться, пока все эти задачи не будут решены.

...