асинхронный параллельный цикл слишком быстро возвращается за пределы задачи, если я не выполняю цикл и проверяю полный логический - PullRequest
0 голосов
/ 29 августа 2018

Мои функции очень быстро вернут информацию. это прекрасные новости! Тем не менее, поскольку параллельный цикл выполняется асинхронно, функция возвращает значение до его завершения, если только я не выполнил какое-либо долгосрочное задание в основном потоке, чтобы дождаться результата. Пользовательский интерфейс для удержания отсутствует, поэтому я использую async / await, чтобы получить быстрый результат в TPL.

Я ввел логическое значение флага и цикл while для ожидания результата.

это работает, но кажется странным.

Есть ли лучший способ «дождаться» результата в моей конкретной ситуации. В первом фрагменте кода, когда я использую цикл while, все кажется странным.

Примечание: я должен упомянуть, что, поскольку это ответ Alexa, вне этой функции находится задача, которая «Task.Delays» в течение восьми секунд, а затем возвращает ответ, если моя другая задача занимает много времени, и Alexa собирается тайм-аут.

private static string SavedImageAnalysisResult(IReadOnlyCollection<Image> savedImageList, ConfigurationDto config)
    {
        string result = "[]";
        BreakImageAnalysis = false;

        if (!savedImageList.Any()) return result;

        Parallel.ForEach(savedImageList, new ParallelOptions
        {
            MaxDegreeOfParallelism = 5000
        },
            async (image, loopState) =>
            {
                Task.Run(() => Console.Write("██"));

                string threadLocalAnalysisResult =
                    await AnalyzeImageAsync(image.ImageBytes, config);

                if (IsEmptyOrErrorAnalysis(threadLocalAnalysisResult)) return;
                Task.Run(() => Console.Write("█ █"));
                result = threadLocalAnalysisResult;
                BreakImageAnalysis = true;
                loopState.Break();
            });

        while (!BreakImageAnalysis) if (BreakImageAnalysis) break; //strange to do this?

        return result;
    }

Эта функция вызывается так:

public static List<Person> DetectPersonAsync()
    {
        Task.Run(() => Console.WriteLine("{0}\nNew Person Detection Requested...", DateTime.Now.ToString("f")));

        ConfigurationDto config = Configuration.GetSettings();

        camera = new SecurityCamera();

        byte[] imageData = camera.GetImageAsByte(config.SecurityCameraUrl +
                                                 config.SecurityCameraStaticImage +
                                                 DateTime.Now);

        if (!imageData.Any()) return null;

        string imageAnalysis = "[]";

        SavedImageList = camera.ImageCache;

        Task.Run(() => Console.WriteLine("\nBegin Image Analysis...\n"));

        var imageAnalysisTasks = new[]
        {
            Task.Factory.StartNew(() => SavedImageAnalysisResult(SavedImageList, config)),
            Task.Factory.StartNew(() => InProgressImageAnalysisResult(camera, config))
        };

        Task.WaitAll(imageAnalysisTasks);

        Task.Run(() => Console.WriteLine("\n\nAnalysis complete\n"));

        if (!IsEmptyOrErrorAnalysis(imageAnalysisTasks[0].Result))
            imageAnalysis = imageAnalysisTasks[0].Result;

        if (!IsEmptyOrErrorAnalysis(imageAnalysisTasks[1].Result))
            imageAnalysis = imageAnalysisTasks[1].Result;

        return !IsEmptyOrErrorAnalysis(imageAnalysis)
            ? JsonConvert.DeserializeObject<List<Person>>(imageAnalysis)
            : new List<Person>();
    }

Но эта функция называется так:

  if (alexa.IsLaunchRequest(alexaRequest))
   {
    //We don't want to wait for these two tasks to return
    Task.Run(() => SendSecurityImageToMagicMirrorUi());
    Task.Run(() => alexa.PostDirectiveResponseAsync(alexaRequest));

     //On your marks get set go! 8 seconds and counting
            return await Task.WhenAny(new[]
           {

               Task.Run(() => GetAlexaCognitiveResponseAsync()),
               Task.Run(() => AlexaRequestTimeoutMonitor())

      }).Result;
     }

И, наконец, есть функция Timeout, которая будет возвращаться, если 8 секунд истекло:

 private static async Task<object> AlexaRequestTimeoutMonitor()
    {
        await Task.Delay(new TimeSpan(0, 0, 0, 8));

        return AlexaApi.ResponseBuilder(CreateNoPersonDetectionPhrase(new AlexaSynthesisResponseLibrary()), false);
    }

Он находится в функции CreateNoPersonDetectedPhrase, в которой я возвращаю логический флаг IsFound обратно в «false»

1 Ответ

0 голосов
/ 29 августа 2018

Ваш Parallel.ForEach запускает асинхронные делегаты, и в действительности они выполняются параллельно до точки await AnalyzeImageAsync, которая возвращает Task в качестве обещания. Теперь, имея это обещание, Parallel.ForEach «думает», что эта задача выполнена, тогда как в действительности асинхронная операция, скорее всего, только начинается. Но никто не ждет этого, поэтому Parallel.ForEach завершается очень быстро. Таким образом, вам нужно выставить эти обещания вне цикла Parallel.ForEach, чтобы их можно было ожидать, например, с помощью Task.WhenAll.

Но я бы также посоветовал подумать, нужна ли вам эта параллелизация вообще, поскольку, если большая часть операции AnalyzeImageAsync (которая здесь не представлена) не связана с ЦП, разумнее использовать только асинхронность.

Самый простой способ (я не говорю, что лучше) ждать асинхронных задач в Parallels.Forxxx

    public static async Task SomeAsyncTask()
    {
        await Task.Delay(5000);
        Console.WriteLine("2222");
    }

    public static async Task Loop()
    {
        var collection = new[] { 1, 2, 3, 4 };
        var asyncTasks = new Task[4];

        Parallel.ForEach(collection,
            (item, loop, index) =>
            {
                Console.WriteLine("1111");
                asyncTasks[index] = SomeAsyncTask();
            });

        await Task.WhenAll(asyncTasks);
    }

Обратите внимание, что все после SomeAsyncTask() должно быть перемещено в продолжение задачи (в данном случае это Console.WriteLine("2222")).

...