【问题标题】:How do I control the access of multiple threads to a collection of objects?如何控制多个线程对对象集合的访问?
【发布时间】:2010-02-26 20:53:22
【问题描述】:

我正在编写一个显示对象列表的应用程序,用户可以选择这些对象列表,然后通过 PropertyGrid 控件查看和编辑其属性。通过使用辅助线程从文件中提取信息的耗时过程填充对象的属性。但我还希望允许用户在提取过程进行时继续查看其他对象。

阅读对 SO 上的my previous 问题的回复后。这听起来像是因为提取过程正在写入的属性不与用户通过属性网格编辑的属性相交,我不应该对同时编辑对象的两个线程有​​问题。虽然如果用户非常不幸并且属性网格最终在非原子写入中间读取对象,则用户可能会看到不正确的值。

但是,我仍然想知道如何设置它以防止用户编辑或查看正在被提取的对象。我对多线程非常陌生,但是我读过的大多数示例都显示了一个单独的令牌对象被创建用于锁定对实际感兴趣对象的访问。我的另一个previous question 的答案证实,通常会创建一个像这样专门用于锁定的单独对象。

所以现在我想知道的是,在我拥有大量对象的情况下如何处理?我想创建锁,以防止属性网格显示用户选择的对象(如果它当前被提取到)。

我是否需要创建一个单独的锁定对象集合以与我的真实集合保持同步?因此,如果从我的主集合中添加或删除一个对象,我必须从我的锁定集合中添加或删除锁定对象?

我是否锁定到实际对象而不是创建单独的令牌锁定对象?

向属性网格可以检查对象是否正在写入的对象添加“IsBeingExtracted”布尔属性怎么样?这将在提取过程的最开始和最结束时设置。

或者在某处引用当前正在提取的当前(如果有)对象的静态字段怎么样。然后属性网格可以检查它被要求显示的最新对象不是这个静态字段引用的对象吗?如果有多个提取线程,这当然行不通。

最好/正确的方法是什么?我个人最喜欢布尔属性选项,但想知道其他真正知道自己在做什么的人的想法。

【问题讨论】:

    标签: c# winforms multithreading locking propertygrid


    【解决方案1】:

    你不能把保存对象的集合设为SynchronizedCollection<T>吗?它将确保一次只有一个线程可以添加或访问对象。然后,不必担心如何同步对每个对象的属性的访问,在对象被填充之前不要将其添加到集合中。

    类似这样的:

    private readonly ICollection<Item> Items = new SynchronizedCollection<Item>();
    
    // Run this on the background thread.
    public void PopulateItems()
    {
        using (var file = File.OpenRead("BigFile.txt"))
        using (var reader = new StreamReader(file))
        {
            while (!reader.EndOfStream)
            {
                var item = new Item();
                PopulateItem(item);
                Items.Add(item);
            }
        }
    }
    
    public void PopulateItem(Item item)
    {
        // Do time-consuming work.
    }
    

    属性网格可以很高兴地对对象做任何事情,因为当它出现在列表中时,提取线程已经完成了它。不需要显式锁定。

    【讨论】:

      【解决方案2】:

      将您的对象集合设为字典,然后锁定键。

      【讨论】:

        【解决方案3】:

        一般来说,使用一个或多个锁取决于您想要达到的并发程度。

        您拥有的锁越少(例如,一个全局锁),并发性就越少,因为许多操作可以竞争锁并阻止彼此运行。

        您拥有的锁越多,您可以实现的并发性越多,但管理锁的开销就越大。

        如果你用锁保护的只是这个对象,那么使用被修改对象的锁是有意义的。您会看到许多使用单独对象的示例,因为您不需要考虑保护范围。

        在您的具体情况下,我不太了解受保护的内容。您是否试图阻止对象属性的并发修改,即后台进程或用户? 由于用户一次只能做一件事,因此不需要很多并发,因此不需要有很多锁。 您可以做的是在属性网格进入编辑模式以及后台进程即将设置正在编辑的相同属性时使用单个锁(否则,不需要锁)。

        【讨论】:

          【解决方案4】:

          我认为最好的方法是使用 ManualResetEvent 对象超时。这样您就可以告诉用户它正在被提取并在几秒钟后重试。

          public class Item : IDisposable // I know it is a horrible class name...
          {
              private readonly ManualResetEvent accessibleEvent = new ManualResetEvent(false);
          
              public void Extract()
              {
                  try
                  {
                      // .....
                  }
                  finally
                  {
                      accessibleEvent.Set(); // unlock             
                  }
              }
          
              public void Edit()
              {
                  if (!accessibleEvent.WaitOne(1000)) // wait 1 second
                  {
                      // notify user?    
                  }
          
                  // ....
              }
          
              public void Dispose()
              {
                  ((IDisposable)accessibleEvent).Dispose();
              }
          }
          

          【讨论】:

          • 为什么锁必须保持一秒钟或更长时间?同步线程的一部分是尽量减少它们可能争用的时间长度。
          • @Rory - 这是等待编辑项目的时间量,而不是保持锁定的时间量。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-09
          相关资源
          最近更新 更多