C# 以线程安全的方式枚举列表

C# 以线程安全的方式枚举列表,c#,.net,multithreading,thread-safety,C#,.net,Multithreading,Thread Safety,假设我在一个类中有一个列表,它将在多线程场景中使用 public class MyClass { List<MyItem> _list= new List<MyItem>(); protected object SyncRoot { get { return ((IList)_list).SyncRoot; } } public void Execute1() {

假设我在一个类中有一个列表,它将在多线程场景中使用

public class MyClass
{
     List<MyItem> _list= new List<MyItem>();
     protected object SyncRoot { 
      get {
         return ((IList)_list).SyncRoot;
        }
     }

     public void Execute1()
     {
        lock(SyncRoot)
        {
             foreach(var item in _list) DoSomething(item);
        }
     }

     public void Execute2()
     {
         Item[] list;
         lock(SyncRoot)
         {
            list=_list.ToArray();
         }
           for(var i=0;i<list.Length;i++) DoSomething(list[i]);
      }
}
公共类MyClass
{
列表_List=新列表();
受保护对象同步根{
得到{
返回((IList)\u列表).SyncRoot;
}
}
public void Execute1()
{
锁定(同步根)
{
foreach(清单中的var项目)DoSomething(项目);
}
}
public void Execute2()
{
第[]项清单;
锁定(同步根)
{
list=_list.ToArray();
}

对于(var i=0;i它是安全的,只要
\u list
的每一次其他使用也使用相同的
lock
语句进行保护。您以独占方式访问列表,复制其内容,然后处理副本(由于作用域,您也可以以独占方式访问该副本).乍一看有点浪费,但在某些情况下这是一种合法的方法。

在两种情况下访问(副本)列表都是线程安全的。但当然,MyItem元素不会以任何方式同步


第二个表单看起来有点贵,但它允许在运行
DoSomething()
s时添加/删除原始表单。该数组就像一种快照,如果符合您的要求,它可能会很有用。请注意,您最好使用
ToList()

是的,从列表中添加/删除项目使用相同的锁。但是我不太明白为什么本地列表会自动获得独占访问权。@MikeSW:因为除了方法
Execute2
,其他所有人都无法访问它。不,如果MyItems是引用类型(对象),Execute2是不安全的,因为列表和数组最终都指向相同的对象。@在这个特定的场景中,这不是问题,因为我只关心列表本身。如果需要,对象可以自己处理同步。@HenkHolterman我知道我可以使用任何对象,但我想知道使用列表的同步对象是否更好。我的意思是,这就是为什么它会这样s暴露了,不是吗?!这个网站有一套很棒的多线程章节: