Getting weird result while using Task Parallel Lib

2019-07-24 05:35发布

I am trying to do some filter task using TPL. Here I am simplifying the code to filter number based on condition. Here is the code.

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

Here, it is simple code to filter out integer which is divided by two or three. Now, if I remove that thread sleep code work perfectly but if I put that it is not.

Normally means without Thread.Sleep , both condition execute 10 times e.g. for every number. But if I add Thread.Sleep first condition executes 7 times and second executes thirteen times. And because of this few number skip the condition. I try to debug but didn't get anything that can point out issue with my code.

Is there any good way to achieve this? Like filter condition on data can work async and parallel to improve performance ?

Code is for demo purpose only.

FYI: Currently I am using Mono with Xamarine studio on windows machine.

Please let me know if any further details needed.

2条回答
对你真心纯属浪费
2楼-- · 2019-07-24 06:03

First you can change your getConditions method to see what's happening inside :

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

And if you stop capturing the foreach's variables, it will work :

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();
}
查看更多
我只想做你的唯一
3楼-- · 2019-07-24 06:06

I would guess it has to do with how your task's lambda closes over the loop variable condition. Try changing it as follows:

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

Note you're also closing over the loop variable item, which could cause similar problems.

查看更多
登录 后发表回答