2

TPL を使用してフィルター タスクを実行しようとしています。ここでは、条件に基づいて数値をフィルタリングするコードを単純化しています。これがコードです。

public static void Main (string[] args)
    {
        IEnumerable<int> allData = getIntData ();

        Console.WriteLine ("Complete Data display");
        foreach (var item in allData) {
            Console.Write(item);
            Console.Write(" | ");
        }

        Console.WriteLine ();
        filterAllDatas (ref allData, getConditions ());

        foreach (var item in allData) {
            Console.Write(item);
            Console.Write(" | ");
        }
        Console.WriteLine ();
    }

    static void filterAllDatas(ref IEnumerable<int> data, IEnumerable<Func<int,bool>> conditions)
    {
        List<int> filteredData = data.ToList ();
        List<Task> tasks = new List<Task>();
        foreach (var item in data.AsParallel()) {
            foreach (var condition in conditions.AsParallel()) {

                tasks.Add(Task.Factory.StartNew(() => {
                    if (condition(item)) {
                        filteredData.Remove(item);
                    }
                }));

            }
        }
        Task.WaitAll(tasks.ToArray());
        data = filteredData.AsEnumerable ();
    }
    static IEnumerable<Func<int,bool>> getConditions()
    {
        yield return (a) => { Console.WriteLine("modulo by 2"); return a % 2 == 0;};
        yield return (a) => { Console.WriteLine("modulo by 3"); Thread.Sleep(3000); return a % 3 == 0;};

    }
    static IEnumerable<int> getIntData ()
    {
        for (int i = 0; i < 10; i++) {
            yield return i;
        }
    }

ここでは、整数を 2 または 3 で割ったものを除外する単純なコードです。さて、そのスレッドスリープコードを削除すると完全に機能しますが、それを入れるとそうではありません。

通常は Thread.Sleep がないことを意味し、両方の条件が 10 回実行されます。たとえば、数値ごとに実行されます。しかし、Thread.Sleep を追加すると、最初の条件が 7 回実行され、2 番目の条件が 13 回実行されます。そして、この数が少ないため、条件をスキップします。デバッグしようとしましたが、コードの問題を指摘できるものは何も得られませんでした。

これを達成する良い方法はありますか?データのフィルター条件と同様に、パフォーマンスを向上させるために非同期および並列に機能できますか?

コードはデモのみを目的としています。

参考までに: 現在、私は Windows マシンの Xamarine スタジオで Mono を使用しています。

さらに詳細が必要な場合はお知らせください。

4

2 に答える 2

2

タスクのラムダがループ変数を閉じる方法に関係していると思いますcondition。次のように変更してみてください。

        foreach (var condition in conditions.AsParallel()) {
            var tasksCondition = condition
            tasks.Add(Task.Factory.StartNew(() => {
                if (tasksCondition(item)) {
                    filteredData.Remove(item);
                }
            }));

ループ変数も閉じていることに注意してください。itemこれにより、同様の問題が発生する可能性があります。

于 2013-03-15T13:18:21.007 に答える
1

まず、getConditionsメソッドを変更して、内部で何が起こっているかを確認できます。

static IEnumerable<Func<int, bool>> getConditions()
{
    yield return (a) => { Console.WriteLine(a + " modulo by 2"); return a % 2 == 0; };
    yield return (a) => { Console.WriteLine(a + " modulo by 3"); Thread.Sleep(3000); return a % 3 == 0; };
}

そして、foreachの変数のキャプチャを停止すると、機能します:

static void filterAllDatas(ref IEnumerable<int> data, IEnumerable<Func<int, bool>> conditions)
{
    List<int> filteredData = data.ToList();
    List<Task> tasks = new List<Task>();
    foreach (var item in data.AsParallel())
    {
        var i = item;
        foreach (var condition in conditions.AsParallel())
        {
            var c = condition;
            tasks.Add(Task.Factory.StartNew(() =>
            {
                if (c(i))
                {
                    filteredData.Remove(i);
                }
            }));

        }
    }
    Task.WaitAll(tasks.ToArray());
    data = filteredData.AsEnumerable();
}
于 2013-03-15T13:27:40.400 に答える