6

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

次に、スタブコレクションを反復処理し、ブール値が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()です

4

1 に答える 1

6

問題は、IEnumeratorの実装にあります。

    public bool MoveNext()
    {
        index++;

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

PLINQがこれを行う理由は正確にはわかりませんが、コレクションの最後に達した場合でも、MoveNextが複数回呼び出されます。問題は、実装に欠陥があることです。コレクションの最後に達した後でMoveNextを再度呼び出すと、インデックスが-1にリセットされるため、列挙が最初からやり直されます。そのため、無限ループに陥っています。行を削除するだけでindex = -1(そして、列挙の終わりに達したときにインデックスの増分を停止するためにメソッドを少し再考するかもしれません)、それは機能します。

于 2012-07-21T14:37:06.157 に答える