猿问

同步集合 InvalidOperationException/System

我编写了一些类来使用SynchronizedCollection.


class MultithreadTesting

{

    public readonly SynchronizedCollection<int> testlist = new SynchronizedCollection<int>();

    public SynchronizedReadOnlyCollection<int> pubReadOnlyProperty

    {

        get

        {

            return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);

        }

    }


    public void Test()

    {

        int numthreads = 20;

        Thread[] threads = new Thread[numthreads];

        List<Task> taskList = new List<Task>();

        for (int i = 0; i < numthreads / 2; i++)

        {

            taskList.Add(Task.Factory.StartNew(() =>

            {

                for (int j = 0; j < 100000; j++)

                {

                    testlist.Add(42);

                }

            }));

        }


        for (int i = numthreads / 2; i < numthreads; i++)

        {

            taskList.Add(Task.Factory.StartNew(() =>

            {

                var sum = 0;

                foreach (int num in pubReadOnlyProperty)

                {

                    sum += num;

                }

            }));

        }

        Task.WaitAll(taskList.ToArray());

        testlist.Clear();

    }

}

运行它我使用


    MultithreadTesting test = new MultithreadTesting();

    while (true)

        test.Test();

但是代码让我失望System.ArgumentException: 'Destination array was not long enough. Check destIndex and length, and the array's lower bounds.'


如果我尝试使用testlistin foreach,我会得到


System.InvalidOperationException: 'Collection was modified; enumeration operation may not execute.'

然而,MSDN 告诉我们


SynchronizedReadOnlyCollection 类


提供一个线程安全的只读集合,其中包含泛型参数指定类型的对象作为元素。


泛舟湖上清波郎朗
浏览 116回答 1
1回答

MYYA

错误的根本原因是List<T>构造不是线程安全的。让我们看看在构造 new 时会发生什么SynchronizedReadOnlyCollection。异常发生在以下行:return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);正如异常 StackTrace 告诉我们的那样,List<T>..ctor在构建过程中涉及到:at System.Collections.Generic.SynchronizedCollection`1.CopyTo(T[] array, Int32 index)at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)at System.Collections.Generic.SynchronizedReadOnlyCollection`1..ctor(Object syncRoot, IEnumerable`1 list)构造函数中的以下片段List<T>显示了错误发生的位置。代码是从MS 参考源中复制的,我清理了代码中不需要的部分以便于阅读。请注意,在注释 (1) 和 (2) 之间还有其他线程在操作集合:public List(IEnumerable<T> collection) {&nbsp; &nbsp; ICollection<T> c = collection as ICollection<T>;&nbsp; &nbsp; // (1) count is now current Count of collection&nbsp; &nbsp; int count = c.Count;&nbsp; &nbsp; // other threads can modify collection meanwhile&nbsp; &nbsp; if (count == 0)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; _items = _emptyArray;&nbsp; &nbsp; }&nbsp; &nbsp; else {&nbsp; &nbsp; &nbsp; &nbsp; _items = new T[count];&nbsp; &nbsp; &nbsp; &nbsp; // (2) SynchronizedCollection.CopyTo is called (which itself is thread-safe)&nbsp; &nbsp; &nbsp; &nbsp; // Collection can still be modified between (1) and (2)&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; // No when _items.Count != c.Count -> Exception is raised.&nbsp; &nbsp; &nbsp; &nbsp; c.CopyTo(_items, 0);&nbsp; &nbsp; &nbsp; &nbsp; _size = count;&nbsp; &nbsp; }}解决方案testlist在构造 new 时,可以通过锁定修改轻松解决该问题SynchronizedReadOnlyCollection。public SynchronizedReadOnlyCollection<int> pubReadOnlyProperty{&nbsp; &nbsp; get&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; lock (testlist.SyncRoot)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}
随时随地看视频慕课网APP
我要回答