应用程序使用PLINQ AsParallel()挂起。 LINQ没问题

时间:2012-07-21 14:19:50

标签: c# linq plinq

我是LINQ和PLINQ的新手,我正在构建一个测试它们的项目。

存根:

class Stub
{
    private Boolean mytf;
    public Stub()
    {
        Random generator = new Random();
        if (generator.NextDouble() < 0.5)
        {
            mytf = false;
        }
        else mytf = true;
    }

    public Boolean tf
    {
        get
        {
            return mytf;
        }
    }
}

StubCollection:

class StubCollection : IEnumerable
{
    Stub[] stubs;

    public StubCollection(int n)
    {
        stubs = new Stub[n];
        for (int i = 0; i < n; i++)
        {
            stubs[i] = new Stub();
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return new StubIterator(this);
    }
    public class StubIterator : IEnumerator
    {
        private StubCollection sc;
        private int index = -1;
        public StubIterator(StubCollection _sc)
        {
            sc = _sc;
        }
        public bool MoveNext()
        {
            index++;
            if (index < sc.stubs.Length)
            {
                return true;
            }
            else
            {
                index = -1;
                return false;
            }
        }
        public object Current
        {
            get
            {
                if (index <= -1)
                {
                    throw new InvalidOperationException();
                }
                return sc.stubs[index];
            }
        }
        public void Reset()
        {
            index = -1;
        }
    }
}

然后我有一些方法来迭代stubcollection并计算布尔值设置为true的存根数:

的foreach:

Stopwatch sw = new Stopwatch();
Int32 n = 0;
sw.Start();
foreach (Stub s in sc)
  if (s.tf) n++;
sw.Stop();
MessageBox.Show("n:" + n.ToString() + " timer:" + sw.ElapsedMilliseconds.ToString());

它有效

LINQ:

Stopwatch sw = new Stopwatch();
Int32 n = 0;
sw.Start();
var trueStubs = from Stub s in sc
                where s.tf
                select s;
n = trueStubs.Count();
sw.Stop();
MessageBox.Show("n:" + n.ToString() + " timer:" + sw.ElapsedMilliseconds.ToString());

它有效(比foreach慢一点)

PLINQ:

Stopwatch sw = new Stopwatch();
Int32 n = 0;
sw.Start();
var trueStubs = from Stub s in sc.AsParallel()
                where s.tf
                select s;
n = trueStubs.Count();
sw.Stop();
MessageBox.Show("n:" + n.ToString() + " timer:" + sw.ElapsedMilliseconds.ToString());

100%CPU,无结果

为什么呢?唯一的区别是AsParallel()

1 个答案:

答案 0 :(得分:6)

问题出在您的IEnumerator实现中:

    public bool MoveNext()
    {
        index++;

        if (index < sc.stubs.Length)
        {
            return true;
        }
        else
        {
            index = -1;
            return false;
        }
    }

我不确定为什么PLINQ会这样做,但是即使到达集合的末尾,也会多次调用MoveNext。问题是您的实现存在缺陷:在到达集合结束后再次调用MoveNext时,索引将重置为-1,因此枚举将重新开始。这就是为什么你陷入无休止的循环。只需删除index = -1行(并且可能会稍微重新考虑该方法以在达到枚举结束时停止递增索引)并且它将起作用。