Application hangs using PLINQ AsParallel(). No problems with LINQ
Asked Answered
V

1

6

I'm new to LINQ and PLINQ, and I'm building a project to test them.

Stub:

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;
        }
    }
}

then I have some methods to iterate the stubcollection and count how many stubs have the boolean set to 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());

it works

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());

it works (little slower than 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, no result

why? the only difference is the AsParallel()

Vagary answered 21/7, 2012 at 14:19 Comment(3)
You "LINQ" version of the foreach is expected to be smaller, because you're creating a new collection then calling count on that new collection--more work. You can do someting more similar to your foreach with LINQ with the following: "int n = sc.Count(s=>s.tf);"Theola
Also, why not just use List<Stub> instead of creating your own collection? (you would have never had this problem had you just used List<Stub>)Theola
Thanks for the count suggestion. I'm creating my own collection just to try to implement IEnumerable.Vagary
E
6

The problem is in your IEnumerator implementation:

    public bool MoveNext()
    {
        index++;

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

I'm not exactly sure why PLINQ does this, but MoveNext is called multiple times even when the end of the collection is reached. The problem is that your implementation is flawed: when calling MoveNext again after reaching the end of the collection, index will be reset to -1, so the enumeration will start over. That's why you're stuck in an endless loop. Just remove the index = -1 line (and maybe rethink the method a little to stop incrementing index when the end of the enumeration is reached) and it'll work.

Elated answered 21/7, 2012 at 14:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.