2013-03-15 56 views
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; 
     } 
    } 

这里,过滤掉被二或三除的整数是简单的代码。现在,如果我删除该线程睡眠代码完美的工作,但如果我把它不是。

通常意味着没有Thread.Sleep,两个条件都执行10次例如为每个数字。但如果我添加Thread.Sleep第一个条件执行7次,第二个执行13次。而由于这几个号码跳过这个条件。我尝试调试,但没有得到任何可以指出我的代码问题。

有没有什么好的方法来实现这个目标?像数据过滤条件可以工作异步和并行来提高性能?

代码仅用于演示目的。

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

请让我知道是否需要任何进一步的细节。

+0

如何以及在哪里是'getData'界定? –

+0

@ GennadyVanin - 新西伯利亚对于错字抱歉...我编辑它... – kunjee

回答

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(); 
} 
+0

选择你的答案作为答案,因为你解释的东西。请马上提出马特的答案。 :) – kunjee

2

我想这与您的任务的lambda关闭循环变量condition的方式有关。试着改变它,如下所示:

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

注意你也关闭了循环变量item,这可能导致类似的问题。

+0

它的工作...该死,我陷入这种类型的陷阱......谢谢......但我没有得到你所说的关闭循环,如果你可以解释,那将是很棒的。 – kunjee