我写了一些类来使用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类
提供一个线程安全的只读集合,其中包含以下对象 由通用参数指定为元素的类型。
答案 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