正确锁定线程安全的自生成列表(C#)

时间:2013-01-07 03:29:19

标签: c# multithreading locking ienumerable generator

我有一个生成一系列数字的单例IEnumerable。序列是可互换的(基本上是无限期的),只有在需要时我才会在序列中生成下一个数字。

public class Generator:IEnumerable<long> {

    private Generator() { }

    private static volatile Generator instance=new Generator();
    private static readonly object syncRoot=new object();
    public static Generator Instance { get { return instance; } }

    private static List<long> numsList=new List<long>();

    private void GenerateNextNumber() {
        long number;
        //Code to generate next number
        numsList.Add(number);
    }

    private long GenerateToNthNumber(int n) {
        lock(syncRoot) {
            while(numsList.Count<n)
                GenerateNextNumber();
        }
        return numsList[n-1];
    }

    public static long GetNthNumber(int n) {
        return Instance.GenerateToNthNumber(n);
    }

    private class GeneratorEnumerator:IEnumerator<long> {
        private int index=0;

        public long Current { get { return GetNthNumber(index); } }

        public void Dispose() { }

        object System.Collections.IEnumerator.Current { get { return GetNthNumber(index); } }

        public bool MoveNext() {
            index++;
            return true;
        }

        public void Reset() {
            index=0;
        }
    }

    public IEnumerator<long> GetEnumerator() {
        return new GeneratorEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
        return GetEnumerator();
    }
}

此代码用于枚举并汇总并发线程中的数字。有没有办法防止每次调用GenerateToNthNumber时都要锁定?我试过这段代码:

    private long GenerateToNthNumber(int n) {
        if(numsList.Count<n) {
            lock(syncRoot) {
                while(numsList.Count<n)
                    GenerateNextNumber();
            }
        }
        return numsList[n-1];
    }

但是,当测试枚举并汇总多个并发线程中的数字时,并非所有结果都以相同的总和结束。如果要求的号码已经生成,我的目标是进行非阻塞读取,如果可能的话。有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

实现List的方式,它在一个线程中被写入另一个线程时无法安全读取。我建议您使用嵌套的已知大小数组,一旦分配,它们永远不会被放弃(例如,一旦分配了一个将保存theList[15691]的数组,该项将永远不会被任何其他数组保留)。这些东西可以很容易地用来实现一个只添加列表,在添加项目时需要锁定,但本质上是线程安全的,无需锁定即可读取。

答案 1 :(得分:0)

您是否考虑过使用线程安全集合?

http://msdn.microsoft.com/en-us/library/dd997305.aspx