5

レコードのリストをループし、各レコードのエクスポート タスクを開始し、タスクが終了するたびに進行状況カウンターを 1 ずつ増やすコードをいくつか用意して、ユーザーがプロセスの進行状況を把握できるようにします。

しかし、ループのタイミングによっては、低い数値の前に高い数値を示す出力が表示されることがよくあります。

たとえば、次のような出力が表示されることを期待しています。

A のエクスポート
B のエクスポート
C のエクスポート
D のエクスポート
E のエクスポート
終了 1 / 5
終了 2 / 5
終了 3 / 5
終了 4 / 5
終了 5 / 5

しかし、代わりにこのような出力が得られます

A のエクスポート
B のエクスポート
C のエクスポート
D のエクスポート
E のエクスポート
終了 1 / 5
終了 2 / 5
終了 5 / 5
終了 4 / 5
終了 3 / 5

値を更新/使用するときに値をロックしていないため、出力が正確であるとは思いません(同じ数値を2回出力したり、数値をスキップしたりする場合があります)が、になるとは思いません。

私のテスト データ セットは 72 個の値で、関連するコードは次のようになります。

var tasks = new List<Task>();
int counter = 0;

StatusMessage = string.Format("Exporting 0 / {0}", count);

foreach (var value in myValues)
{
    var valueParam = value;

    // Create async task, start it, and store the task in a list
    // so we can wait for all tasks to finish at the end
    tasks.Add(
        Task.Factory.StartNew(() =>
        {
            Debug.WriteLine("Exporting " + valueParam );

            System.Threading.Thread.Sleep(500);
            counter++;
            StatusMessage = string.Format("Exporting {0} / {1}", counter, count);

            Debug.WriteLine("Finished " + counter.ToString());
        })
    );
}

// Begin async task to wait for all tasks to finish and update output
Task.Factory.StartNew(() =>
{
    Task.WaitAll(tasks.ToArray());
    StatusMessage = "Finished";
});

出力は、デバッグ ステートメントとStatusMessage出力の両方で逆方向に表示できます。

この問題が発生しないように、ループ内の完了した非同期タスクの数を数え続ける正しい方法は何ですか?

4

2 に答える 2

7

Debug.WriteLine(...)メソッドが実行されるのと同じ順序でカウンターがインクリメントされないため、混合出力が得られます 。

一貫した進行状況レポートを取得するには、レポート ロックをタスクに導入できます。

tasks.Add(
    Task.Factory.StartNew(() =>
    {
        Debug.WriteLine("Exporting " + valueParam );

        System.Threading.Thread.Sleep(500);
        lock(progressReportLock)
        {
           counter++;
           StatusMessage = string.Format("Exporting {0} / {1}", counter, count);
           Debug.WriteLine("Finished " + counter.ToString());
        }
    })
);
于 2013-04-01T15:04:31.390 に答える