2012-04-10 84 views
1

假设我在一个将在多线程场景中使用的类中有一个列表。以线程安全的方式枚举列表

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]); 
     } 
} 

的方法Execute1是“正常”的方式来枚举一个线程安全的方式列表。但是Execute2?这种方法仍然是线程安全的吗?

+1

不,如果MyItems是引用类型(对象),Execute2是不安全的,因为列表和数组最终都指向相同的对象。 – Will 2012-04-10 07:34:17

+0

@Will这在这个特定的场景中不是问题,因为我只关心列表本身。如果需要的话,这些对象可以自己处理同步。 – MikeSW 2012-04-10 07:52:05

+0

@HenkHolterman我知道我可以使用任何对象,但我想知道是否最好使用列表的同步对象。我的意思是这就是为什么它暴露,不? – MikeSW 2012-04-10 07:53:45

回答

1

只要_list的其他用途也使用相同的lock声明进行保护,则它是安全的。您正在对列表进行独占访问,复制其内容,然后处理副本(由于范围限制,您也可以对其进行独占访问)。一见钟情有点浪费,但在某些情况下是合法的做法。

+0

是的,使用同一个锁来添加/删除列表中的项目。不过,我不太明白为什么本地列表会自动获得独占访问权限。 – MikeSW 2012-04-10 07:21:34

+0

@MikeSW:因为它除了方法'Execute2'之外的其他人都超出了范围。 – Jon 2012-04-10 07:27:11

2

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

第二种形式看起来有点贵,但它会允许在运行DoSomething()时在原件上添加/删除。该数组就像一种快照,如果这符合您的要求,它可能会有用。请注意,您不妨使用ToList()