【问题标题】:Can I use parallel.ForEach to call different functions?我可以使用 parallel.ForEach 来调用不同的函数吗?
【发布时间】:2012-04-17 15:20:15
【问题描述】:

我有一个运行的 foreach 循环。我正在研究并行函数。是否可以将以下代码转换为使用并行编程?

int result ;
int counter;
foreach(DataRow dr in ds.Tables[0].Rows) {
    switch(dr["Gender"].ToString()) {
        case "Male":
            result = functionMale(dr["Gender"].ToString());
            counter += result;
            break;
        case "Female":
            result = functionFemale(dr["Gender"].ToString());
            counter += result;
            break;
        default:
            result = functionUnkown(dr["Gender"].ToString());
            counter += result;
            break;
    }
}

根据我的调查,到目前为止,我只有以下内容。

Parallel.ForEach(ds.Tables[0].AsEnumerable(), dr => {
    var result = functionMale(dr["Gender"].ToString();
});

有什么想法吗?谢谢

【问题讨论】:

标签: c# asp.net .net multithreading parallel-processing


【解决方案1】:

您可以使用AsParallelSum

Func<string, int> calculateGender =
    gender =>
    {
        // nb: I don't know why you pass the gender to the method, but
        // I've left your intent as-is
        switch (gender)
        {
            case "Male":   return functionMale(gender);
            case "Female": return functionFemale(gender);
            default:       return functionUnknown(gender);
        }
    };

int counter = ds.Tables[0].AsEnumerable()
                          .AsParallel()
                          .Sum(dr => calculateGender(dr["Gender"].ToString()));

【讨论】:

  • 我有点困惑。 @Dave 也在做同样的事情。那么哪个更快?
  • @PankajGarg:我不知道,在实践中可能没有区别。
  • 幕后可能完全一样。
  • @sixlettervariables:这会减少处理时间吗?
  • @MrM:这一切都取决于你有多少行,AsEnumerable 需要多长时间,每个性别计算需要多长时间等等。有时工作负载并没有更快地并行化,有时它们甚至更慢。如果不尝试,您可能不会知道。
【解决方案2】:

你可以尝试这样的东西,风格更实用:

var counter = 
    ds.Tables[0].AsEnumerable()
    .AsParallel()
    .Select( dr => {
        var gender = dr["Gender"].ToString();

        switch(gender) 
        {
            case "Male":
                return functionMale(gender);
            case "Female":
                return functionFemale(gender);
            default:
                return functionUnkown(gender);
        }
    })
    .Sum();

【讨论】:

  • 是不是先(内部)选择列表中的所有记录,然后选择求和函数?
  • 是的 - 但 select 被称为“按原样” - 所以它可能与上面来自 Sixlettervariables 的其他答案具有相同的性能配置文件
【解决方案3】:

当然,这完全有可能。 Parallel.ForEach 在这个实例中没有做任何特定的魔法(除了线程),所以它看起来像这样:

ds.Tables[0].Rows.AsEnumerable().AsParallel().Sum(x =>
  {
       DataRow dr = x as DataRow;

       switch(dr["Gender"].ToString())
       {
           case "Male":
             // Stuff
           case "Female";
             // Stuff
           default:
             // Stuff
       }

       return counter;
  });

这应该聚合函数的所有值,因为加法是可交换的。

【讨论】:

  • 他们确实有这样的依赖;他正在汇总数据。
  • 更新了计数器代码;我的错,我没有看到聚合。
【解决方案4】:

PLINQ 方法要容易得多,但这里只是为了完善答案,您将如何使用 Parallel.ForEach 来做到这一点。

int counter = 0;
Parallel.ForEach(ds.Tables[0].AsEnumerable(),
  () => /* subtotal initializer */
  {
    return 0; 
  },
  (dr, state, subtotal) => /* loop body */
  {
    switch(dr["Gender"].ToString()) 
    { 
        case "Male": 
            subtotal += functionMale(dr["Gender"].ToString()); 
            break; 
        case "Female": 
            subtotal += functionFemale(dr["Gender"].ToString()); 
            break; 
        default: 
            subtotal += functionUnkown(dr["Gender"].ToString()); 
            break; 
    }
  },
  subtotal => /* subtotal accumulator */
  {
    Interlocked.Add(ref counter, subtotal);
  });

这是它的工作原理。第一个 lambda 表达式为 TPL 分配的每个工人初始化本地小计箱。第二个 lambda 表达式对集合中的每个项目执行一次,并更新本地小计 bin。第三个 lambda 表达式将局部小计 bin 组合到最终总数中。

Parallel.ForEachAsParallel 与聚合一起使用的一个有趣区别在于小计累积到最终值的方式。 Parallel.ForEach 在工作线程上执行此操作,因此需要线程安全操作 Interlocked.AddAsParallel 使用相同的分区策略,但在调用者线程上累积小计。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-12
    • 2017-07-22
    • 2021-01-21
    • 1970-01-01
    • 2013-01-23
    相关资源
    最近更新 更多