【问题标题】:Can I use dictionary elements as lock objects?我可以将字典元素用作锁定对象吗?
【发布时间】:2013-06-07 20:55:52
【问题描述】:

我有多个队列正在被多个线程访问。为了实现线程安全,我做了以下事情:

private static Dictionary<string, Queue<string>> MyQueues = new Dictionary<string, Queue<string>>();

public static string GetNextQueueElementForKey(string key)
{
    string res = string.Empty;

    if (MyQueues.Keys.Contains(key))
    { 
       Queue<string> queue = MyQueues[key];
       lock (queue)
       {
           if (queue.Count() > 0)
           {
               res = queue.Dequeue();
           }
       }
   }

   return res;
}

我也可以锁定MyQueues,但是我会锁定更多的东西。所以我的问题是,如果锁定字典中包含的对象会起作用 - 假设键的值(队列)永远不会改变。

【问题讨论】:

  • 您的问题更像是一个陈述。你还有什么问题?
  • 您不会锁定对象,而是锁定对对象的引用。所以换句话说,对象的值是否改变并不影响锁。
  • @Nolonar 不,您锁定了对象本身。你可以有 17 个对同一个对象的引用——它们都会锁定同一个东西。
  • @Yogee 我看不出GetHashCode() 在这里有什么相关性,或者为什么有人想在哈希码上使用ToString() - 事实上你几乎肯定应该从不 锁定string - 你能澄清一下你在说什么吗?
  • @yogee:不锁定对象的地址;垃圾收集器可以移动对象的地址!它锁定被引用对象中的同步块。当然,这是一个实现细节。

标签: c# multithreading locking


【解决方案1】:

可以 - 但我一般不会。就个人而言,我通常尝试锁定不用于其他任何事情的普通 System.Object 实例,理想情况下,除了锁定它们的类之外,它们甚至不会暴露于任何代码。这样你就可以绝对确定没有其他东西会被锁定。

在这种情况下,您似乎已经控制了队列,因此您知道它们不会被其他代码使用,但 可能Queue&lt;T&gt; 中的代码将锁定this。可能不是这样,但这是我会担心的事情。

从根本上说,我希望 .NET 没有采用 Java 的“每个对象的监视器”的方法 - 我希望 Monitor 是一个可实例化的类。

(我假设您实际上只是从多个线程读取字典?使用字典进行多线程读/写是不安全的。)

【讨论】:

  • 我只在队列为空时写入。为简单起见,我在上面的代码中省略了该部分。但是在这种情况下应该锁定队列,所以写入它应该没有问题。
  • 但是使用普通的System.Object 不适用于未知数量的锁对象。
  • @Ben:您可以使用专用锁对象的字典,每个Queue&lt;T&gt; 实例对应一个。我仍然不明白你为什么使用Queue&lt;T&gt; 而不是ConcurrentQueue&lt;T&gt;
  • @Ben:你可以创建一个Dictionary&lt;string, Tuple&lt;object, Queue&lt;T&gt;&gt; - 任何时候你创建一个队列,你就创建了一个对象。我不确定我是否会这样做(并且使用ConcurrentQueue&lt;T&gt; 通常是我的首选)但值得考虑。
【解决方案2】:

它是字典中的一个元素这一事实在很大程度上是无关紧要的——只要它是一个引用类型(Queue&lt;string&gt; )——然后每个队列,当从字典中获取时,每次都将是相同的 object 实例。这意味着它可以在每个队列级别锁定完美合理地工作。

所以基本上:是的,这应该可以正常工作 - 只要Enqueue 执行相同的每个队列锁定。正如 Jon 所说 - 你是否应该这样做是另一个问题。

就个人而言,我仍然认为Monitor 应该是一个非静态类型,并且您只能锁定Monitor 实例,而不是任何object

【讨论】:

    【解决方案3】:

    所以我的问题是,如果锁定字典中包含的对象会起作用 - 假设键的值(队列)永远不会改变。

    在这里查看您的代码:

    lock (queue) {
        if (queue.Count() > 0) {
            res = queue.Dequeue();
        }
    }
    

    可以,但我不会这样做。您应该never lock on the object itself,因为您可能会与其他 代码线程竞争,这些线程将锁定同一对象,包括Queue&lt;T&gt; 本身(谁可以锁定this)。

    因此,您至少应该为每个队列创建一个专用的锁对象。

    但是,您不使用ConcurrentQueue&lt;T&gt; 有什么原因吗?那将是最简单的解决方案,并将正确处理它的负担转移到框架。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-29
      • 2016-06-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多