用于累积列表<list <double>&gt; </list <double>的值的并行版本代码

时间:2013-08-13 18:23:59

标签: c# task-parallel-library interlocked

我再次努力去理解并行计算的一些事情。 在我正在处理的代码中,我有一个扩展list<list<double>>的类。在这个类中,我正在编写一个方法来返回list<list<double>>中值的平均值。这种方法是私有的。

public class myClass : list<list<double>>
{
    //properties and stuff
    private double average()
    {
        //method body
    }
}

这个方法我有两个版本,它们都有效。第一个版本是serial:

private double average()
{
    double avg = 0;
    for (int i = 0; i < this.Count; i++)
    {
        for (int j = 0; j < this[0].Count; j++)
        {
            avg += this[i][j];
        }
    }
    avg = avg / (this.Count*this[0].Count);
    return avg;
}

第二个版本是平行的:

private double average()
{
    double avg = 0;
    double[] cumsum = new double[this.Count];
    Parallel.For(0, this.Count, i =>
        {
            cumsum[i] = 0;
            for (int j = 0; j < this[0].Count; j++)
            {
                cumsum[i] += this[i][j];
            }
        });
    avg = cumsum.Sum() / ((this.Count * this[0].Count));
    return avg;
}

作为一项学习练习,我试图通过使用更复杂的并行线程来解决问题。我的想法是在没有中间数组的情况下进行操作。具体来说,这是我的尝试(不起作用):

private double average()
{
    double avg = 0;
    Parallel.For<double>(0, this.Count, () => 0, (i, loop, sub) =>
        {
            for (int j = 0; j < this[0].Count; j++)
            {
                sub += this[i][j];
            }
            return sub;
        },
        (x) => 
            {
                double tot = avg;
                Interlocked.CompareExchange(ref avg, tot+sub, tot);
            });
    return avg / ((this.Count * this[0].Count));
}

这个片段(至少)有两个错误。 它给我的第一个错误是sub += this[i][j];

  

最佳重载方法匹配   'System.Collections.Generic.List&GT;。这[INT]'   有一些无效的论点

我在这里不理解这个错误,因为i和j都是int类型。

然后我在Interlocked.CompareExchange(ref avg, tot+sub, tot);上又出现了一个错误(预计,因为我真的不明白这个方法是如何工作的):

  

当前上下文中不存在名称“sub”

有人能指出我最后一个片段的正确形式吗?和/或一些材料来澄清这些事情?我从http://msdn.microsoft.com/en-us/library/dd460703.aspx读到,但这对我来说没有任何帮助。

2 个答案:

答案 0 :(得分:2)

就个人而言,我会使用:

double sum = yourListOfList.AsParallel().SelectMany(list => list).Average();

为了修复你的方法,你需要在本地最终lambda中使用你的循环状态。 (你目前没有使用x。)

double sum = 0;
var syncObj = new object();
Parallel.For<double>(0, this.Count, () => 0, (i, loop, sub) =>
    {
        var innerList = this[i];
        for (int j = 0; j < innerList.Count; j++)
        {
            sub += innerList[j];
        }
        return sub;
    },
    (x) => 
        {
            lock(syncObj)
               sum += x;
        });
return sum / ((this.Count * this[0].Count));

请注意,您的版本(以及我的“更正”)有许多缺点。假设每个子列表的长度与this[0]的长度相同,这不是由类型保证或建议的。

答案 1 :(得分:1)

引发第一个错误是因为i参数是long类型。这是因为存在两个类似的重载,一个具有int类型的第一个参数,另一个具有long类型。要选择“int”重载,必须显式实例化lambda表达式:

new Func<int, ParallelLoopState, double, double>((i, loop, sub) => { ... })


引起第二个错误是因为子变量在给定范围内不存在。