web-dev-qa-db-ja.com

並列foreachでawaitを使用する方法は?

だから私はこれを理解しようとする夜の良い部分を見つけました。

昨日、parallel.foreachを紹介してくれて幸運でした。細かいところを除いて、私がやりたいように機能します。

私は以下を持っています:

        Parallel.ForEach(data, (d) =>
        {
            try
            {
                MyMethod(d, measurements);
            }
            catch (Exception e)
            {
              // logg
            }

        });

「MyMethod」メソッド内には、実行されるロジックがたくさんあり、そのほとんどは問題ありませんが、API呼び出しを行ってデータをフェッチし、非同期タスクを使用して、「待機」を使用してコードを実行できるようにします特定の部分が実行されるまで待ってから次に進みます:

    private async void MyMethod(PimData pimData, IEnumerable<ProductMeasurements> measurements)
    {
        try
        {
           // alot of logic but most relevant part 

            await Task.WhenAll(ExecuteMeasurmentAndChartLogic(pimData.ProductNumber, entity));
            await Task.WhenAll(resourceImportManager.HandleEntityImageFiles(pimData.ProductType + pimData.ProductSize,SwepImageType.Png, ResourceFileTypes.ThreeD, entity, LinkTypeId.ProductResource));

            await Task.WhenAll(resourceImportManager.HandleEntityImageFiles(pimData.ProductSketch, SwepImageType.Png, ResourceFileTypes.Sketch, entity, LinkTypeId.ProductResource));

        }
        catch (Exception e)
        {
            // logg
        }
    }

問題

1まず、すべてのコードが終了する前にループが終了します

2番目の問題は、多くのAPI呼び出しで「タスクがキャンセルされました」というメッセージが表示されることです

3そして3番目に、上記のように、コードは各メソッドが完全に実行されるのを待ちません。

次のステップに進む前に、ExecuteMeasurmentAndChartLogic()メソッドですべてを実行することはできません。

これにより、次の問題が発生します(その他の問題)。

このメソッドでは、アイテムを作成してdbに追加します。このアイテムには、ExecuteMeasurmentAndChartLogic()内で行われるapi呼び出しから取得する詳細情報が必要ですが、問題は、いくつかのアイテムが作成され、待機する必要があることです。私が望んでいない残りのデータ。

サイドノート:すべてのデータがなくなる前にアイテムを作成してdbに追加することは知っていますが、ベストプラクティスはありませんが、PIMそのためのプロセスはデリケートです

いくつかのスレッドを実行したいのですが、同時に次のメソッドに進む前に、アイテムごとにフルロジックを実行したいのです。

明らかにする:

実行中のいくつかのアイテム

各アイテムはハンドヘルド[〜#〜] all [〜#〜]コードの次の部分に進む前にハンドリングする必要のあるロジックで、待機してこれを正常に行います。

上記のコードでは、ExecuteMeasurmentAndChartLogic()が完了する前に、resourceImportManager()メソッドが実行されます。これは私が望んでいないことです。

Parallel.ForEachの代わりに:

    Task task1 = Task.Factory.StartNew(() => MyMethod(data, measurements));
    Task.WaitAll(task1);

しかし、それはあまり助けにはなりませんでした

これはかなり新しく、どこで間違っているのか理解できませんでした。

編集:これで問題を更新しました

編集:これはExecuteMeasurmentAndChartLogic()がどのように見えるかです:

    public async Task ExecuteMeasurmentAndChartLogic(string productNumber, Entity entity)
    {
        try
        {
            GrafGeneratorManager grafManager = new GrafGeneratorManager();
            var graphMeasurmentList = await MeasurmentHandler.GetMeasurments(productNumber);

            if (graphMeasurmentList.Count == 0) return;

            var chart = await grafManager.GenerateChart(500, 950, SystemColors.Window, ChartColorPalette.EarthTones,
                "legend", graphMeasurmentList);

            await AddChartsAndAddToXpc(chart, entity, productNumber);
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }

    }

編集:これの背景:多くのデータを取得するためにAPIを呼び出します。このデータの各アイテムについて、API呼び出しを行い、アイテムに適用するデータを取得する必要があります。

Alosのコメントを読んだ後、私は別の方法で考えさせられました。私はおそらくすべてのアイテムをループして、それらにマイナーロジックを実行し、タスクリストにURLを追加して、これを1つずつ実行する別のタスクを作成できます。

これを更新し続けます

7
ThunD3eR

Parralel.ForEachは使用しないでください。メソッドをvoidではなくTaskを返すようにし、すべてのタスクを収集して次のように待機します。

Task.WaitAll(data.Select(d => MyMethod(d, someParam)).ToArray());
14
Eduard Lepner