【问题标题】:Enumerating a list in a thread-safe way以线程安全的方式枚举列表
【发布时间】:2012-04-10 07:12:56
【问题描述】:

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

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 呢?这种方法仍然是线程安全的吗?

【问题讨论】:

  • 不,如果 MyItems 是引用类型(对象),则 Execute2 是不安全的,因为列表和数组最终都指向相同的对象。
  • @Will 在这种特定情况下这不是问题,因为我只关心列表本身。如果需要,对象可以自行处理同步。
  • @HenkHolterman 我知道我可以使用任何对象,但我想知道使用列表的同步对象是否更好。我的意思是这就是它暴露的原因,不是吗?!
  • 这个站点有一组很棒的多线程章节:albahari.com/threading

标签: c# .net multithreading thread-safety


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    只要_list 的所有其他使用也受到相同的lock 语句的保护,它就是安全的。您正在对列表进行独占访问,复制其内容,然后处理副本(由于范围界定,您也拥有独占访问权限)。乍一看有点浪费,但在某些情况下是一种合法的方法。

    【讨论】:

    • 是的,从列表中添加/删除项目是使用相同的锁进行的。但是我不太明白为什么本地列表会自动获得独占访问权限。
    • @MikeSW:因为除了方法Execute2之外,它超出了其他所有人的范围。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多