【问题标题】:.NET CLR - Object Reference Synchronization.NET CLR - 对象引用同步
【发布时间】:2011-08-19 14:42:11
【问题描述】:
在多线程 .NET 应用程序中,
假设第一个线程正在写入一个列表
第二个线程正在清除所有项目。
第三个线程正在从列表中读取。
如果第二个和第三个线程在 CLR 级别上“真正”同时访问同一个列表对象会发生什么。我不是指 .NET 同步对象和锁定机制。
我的意思是,当 CLR 从引用(通过第 3 个线程)访问列表项时,如果引用指向的列表发生变化(通过第 2 个线程)会发生什么?
【问题讨论】:
标签:
.net
multithreading
thread-safety
clr
【解决方案1】:
坏东西。
这种类型的公共静态(在 Visual Basic 中为共享)成员是线程安全的。 不保证任何实例成员都是线程安全的。
只要不修改集合,List<T> 可以同时支持多个读取器。通过集合进行枚举本质上不是线程安全的过程。在枚举与一个或多个写访问竞争的极少数情况下,确保线程安全的唯一方法是在整个枚举期间锁定集合。要允许集合被多个线程访问以进行读写,您必须实现自己的同步。
【解决方案2】:
它爆炸了。 InvalidOperationException 被抛出,因为枚举发生了变化。
【解决方案3】:
如果您倾向于更新列表并在多线程代码中读取它,那么可能会发生很多故障,例如,假设我们有以下场景:
//c#
List<object> myList...
//at reading thread
if (myList.Count > 0)
{
object item = myList[0];//get the item at the first index of the collection.
}
//at writing thread
myList.Clear();
写入线程在更新列表的同时读取线程正在从列表中读取,因此假设执行如下:
阅读器线程检查集合中是否有项目,并发现其中有一些项目“.Count > 0 为真”,因此继续执行,但在到达下一行之前,线程上下文切换 通过切换到编写器线程暂停读取器线程,因此它执行其代码myList.Clear();,此时线程上下文切换回读取器线程以继续执行,因此它尝试获取myList[0],但是那个时候,作者线程的集合是空的,所以它将失败并出现异常IndexOutOfRange..
另一种情况,如果读取器线程使用foreach 抛出集合,而写入器线程只是更改了集合“添加/删除”了一些项目,它会再次抛出异常,因为循环时集合发生了变化。
因此,在与列表交互时,您必须使用一些同步机制,例如 C# 中的 lock 或使用 Monitor 类。但是,如果您使用的是 4.0,则可以关闭以使用 ConcurrentCollection 而不是普通列表,它们是线程安全的集合。