SynchronizedCollection InvalidOperationException / System.ArgumentException

时间:2019-04-23 11:30:19

标签: c# list asynchronous concurrency synchronization

我写了一些类来使用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.'

如果我尝试在foreach中使用testlist,我会得到

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

但是,MSDN告诉我们

  

SynchronizedReadOnlyCollection类

     

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

1 个答案:

答案 0 :(得分:1)

错误的根本原因是from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt from matplotlib import cm from matplotlib.ticker import LinearLocator, FormatStrFormatter import numpy as np fig = plt.figure() ax = fig.gca(projection='3d') # Make data. X = np.arange(-5, 5, 0.25) Y = np.arange(-5, 5, 0.25) X, Y = np.meshgrid(X, Y) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) # Plot the surface. surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False) # Customize the z axis. ax.set_zlim(-1.01, 1.01) ax.zaxis.set_major_locator(LinearLocator(10)) ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f')) # Add a color bar which maps values to colors. fig.colorbar(surf, shrink=0.5, aspect=5) plt.show() 的构造不是线程安全的。

让我们看看构造新的List<T>时会发生什么。以下行出现异常:

SynchronizedReadOnlyCollection

StackTrace告诉我们,构造过程涉及return new SynchronizedReadOnlyCollection<int>(testlist.SyncRoot, testlist);

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) 构造函数的以下代码片段显示了发生错误的位置。代码是从MS reference source复制而来的,我清除了不必要的代码部分,以便于阅读。请注意,在注释(1)和(2)之间,还有其他线程在操纵集合:

List<T>

解决方案

在构造新的public List(IEnumerable<T> collection) { ICollection<T> c = collection as ICollection<T>; // (1) count is now current Count of collection int count = c.Count; // other threads can modify collection meanwhile if (count == 0) { _items = _emptyArray; } else { _items = new T[count]; // (2) SynchronizedCollection.CopyTo is called (which itself is thread-safe) // Collection can still be modified between (1) and (2) // No when _items.Count != c.Count -> Exception is raised. c.CopyTo(_items, 0); _size = count; } } 时,可以通过锁定testlist修改轻松解决问题。

SynchronizedReadOnlyCollection