使用任务并行库时获得奇怪的结果?

时间:2013-03-15 10:07:20

标签: c# .net multithreading c#-4.0 task-parallel-library

我正在尝试使用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次,第二个执行十三次。而且由于这几个数字跳过了这个条件。我尝试调试,但没有得到任何可以指出我的代码问题。

有没有什么好方法可以达到这个目的?像过滤条件一样,数据可以异步和并行工作以提高性能吗?

代码仅用于演示目的。

仅供参考:目前我在Windows机器上使用Mono和Xamarine工作室。

如果需要进一步的细节,请告诉我。

2 个答案:

答案 0 :(得分:2)

我猜它与你的任务的lambda如何关闭循环变量condition有关。尝试按如下方式更改它:

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

请注意,您还要关闭循环变量item,这可能会导致类似的问题。

答案 1 :(得分: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();
}