2010-09-29 52 views
1

以下代码将从网站调用,因此在非静态类中有一个静态字典对象需要线程安全。基本上,代码的目的是封装逻辑并维护存储在CounterContainer实例中的perfmon计数器的生命周期。构造函数被称为传入instanceName。构造函数需要检查该实例名称的CounterContainer是否已被定义并存储在字典中。如果是这样,它可以(并且必须)使用该实例。如果不是,它会创建一个CounterContainer实例,将其存储在字典中,然后使用该实例。要使用的CounterContainer实例存储在一个非静态成员中,因此线程安全。这是锁定线程安全的正确对象吗?

作为使用静态字典的唯一地方是在构造函数中,它让我觉得在它将被访问期间锁定字典是安全的吗?这是否会导致任何不可预见的问题,如阻塞/死锁?我什么都看不到,但过去并没有太多需要考虑这种事情。我也考虑过lock(this):但我不认为这样会工作,因为它只会锁定被创建的PerformanceCounters的实例,而不是基础的静态字典(所以不会是线程安全的)。

namespace ToolKit 
{ 
    using System; 
    using System.Diagnostics; 
    using System.Collections.Generic; 

    public class PerformanceCounters : IPerformanceCounters 
    { 
     private static Dictionary<string, CounterContainer> _containers = new Dictionary<string, CounterContainer>(); 
     private CounterContainer _instanceContainer; 

     public PerformanceCounters(string instanceName) 
     { 
      if (instanceName == null) throw new ArgumentNullException("instanceName"); 
      if (string.IsNullOrWhiteSpace(instanceName)) throw new ArgumentException("instanceName"); 

      // Is this the best item to lock on? 
      lock (_containers) 
      { 

       if (_containers.ContainsKey(instanceName)) 
       { 
        _instanceContainer = _containers[instanceName]; 
        return; 
       } 

       _instanceContainer = new CounterContainer(instanceName); 
       _containers.Add(instanceName, _instanceContainer); 
      } 
     } 
     public void Start() 
     { 
      _instanceContainer.AvgSearchDuration.Start(); 
     } 

     public void FinishAndLog() 
     { 
      _instanceContainer.SearchesExecuted.Increment(); 
      _instanceContainer.SearchesPerSecond.Increment(); 
      _instanceContainer.AvgSearchDuration.Increment(); 
     } 
    } 
} 
+2

你有没有考虑过使用ConcurrentDictionary类?那么你不必担心它 - 也可以重复到这个问题:http://stackoverflow.com/questions/440957/c-concurrency-locking-and-dictionary-objects – BrokenGlass 2010-09-29 13:07:40

+0

@BrokenGlass:对不起,我错过了,是的,他们几乎完全相同的情况。在没有听说过ConcurrentDictionary类之前,它也被提出来作为对此的回应。 – 2010-09-29 17:30:14

回答

0

在我看来,相当普遍的是有一个专门用于锁定的显式对象实例。

private readonly object containerLock = new object(); 
... 
lock (containerLock) { 
... 
} 

另一个技巧:如果要针对.NET 4中,可考虑使用ConcurrentDictionary

+0

-1:字典是静态的;你的锁被实例化。 – x0n 2010-09-29 13:10:35

+0

但是,对于引用ConcurrentDictionary的+1,因为GetOrAdd(...)传入一个函数来执行创建的功能看起来正是我想要做的。 @flq:谢谢你,这是值得建立在@ x0n所说的,使用对象的方式与“this”相同,因为两者都是基于实例的。如果你让对象是静态的,那么它就像我提供的代码一样,但是我锁定了我正在使用的确切对象。 – 2010-09-29 13:18:43

+0

将字静态放入,然后 – flq 2010-09-29 13:25:36

2

是的,它会工作,因为_containers是静态的。

你可能想看看ReaderWriterLockSlim以及因此想要阻止theard这么多(改善性能)

+0

+1提醒我关于ReaderWriterLocks,并且有一个瘦身版本。我完全忘记了这些,因为我不得不担心线程问题已经有几年了。将@flq标记为接受的答案,因为我将使用他引用的ConcurrentDictionary。 – 2010-09-29 13:20:14

+0

这很好 - 我以前不知道ConcurrentDictionary ;-)。你每天都会学到...... – 2010-09-29 13:35:10

+1

我*高度*怀疑在这种情况下'ReaderWriterLockSlim'实际上比普通的旧'lock'慢。 – 2010-09-29 13:45:43

0

是,_containers是因为它的作用范围一样的字典实例,从理论上确定这是一个静态的(显然 - 这是字典实例)。

但是,一旦有人担心你的整体设计是你说这是托管在一个网站。每个asp.net worker都是一个独立的进程,因此当IIS由于闲置或其他原因而循环工作进程时,您的静态字典可能会被销毁。此外,您还可以,如果你正在使用Web园有你的字典的多个实例

+0

PERFMON计数器保持活着,只要至少有一个实例。在内存中活动的.NET引用。但即使在内存中存在两个+ .NET引用,它们仍然会增加PERFMON计数器的单个实例。因此,同一台服务器上的多个IIS站点将按我的需要工作。网络农场也很好,因为我想在服务器基础上测量服务器。但谢谢你的提问。 – 2010-09-29 13:26:26

5

不要与ConcurrentDictionary使用的建议不以为然,但更普遍的回答(多个工人每个应用程序。):

  1. 最好的锁定解决方案是根本不锁定的解决方案。有时候,不锁定的情况下并发并发并不困难。其次最好的是拥有细密的锁。然而,粗粒锁更容易推理,因此对其正确性有信心。除非你有一个经过良好测试的无锁类别的合适目的,否则从粗粒度锁定开始(使用相同锁定阻止一堆操作的锁定),然后移动到更细粒度,因为存在一个逻辑上的死锁(你可以看到两个不相关的操作可能会彼此阻塞),或者如果需要优化。
  2. 永远不要锁定在this因为你的代码外部的东西可以锁定对象,你可以很容易地得到一个死锁或至少锁定争夺,只能通过查看类内部的代码和调用代码(以及写人的人可能无法访问其他人)。由于类似的原因,永远不要锁定Type
  3. 由于类似的原因,只锁定私人会员。这样,只有类内部的代码才能锁定它,锁争用的可能性仅限于该地点。
  4. 避免将partial与此类代码一起使用,并且如果您确实使用partial,请尝试将对象上的所有锁保留在同一位置。这里没有技术上的原因,但是当你需要考虑所有可能发生锁定的地方时,它确实有帮助。
  5. 永远不要锁定值类型(整数类型,结构等)。这样做会将值填入新对象并锁定。然后,当其他代码尝试获取锁定时,它会将其锁定到另一个对象并对其进行锁定。基本上根本没有锁(除了理论上的优化,拳击使用flyweight模式[它没有],但实际上会让事情变得更糟)。
  6. 有时,锁的用途将与使用锁时使用的单个对象相关,并且在不使用锁时不使用。在这种情况下,将该对象用作锁定有助于代码的可读性,从而将锁与该对象相关联。
  7. 拥有一个纯粹用于锁定目的的对象永远不会错,所以如果你不确定,就这样做。
  8. 如果任何受影响的对象是静态的,则锁定对象必须是静态的,并且可能是其他情况。相反,由于实例锁比静态锁更精细,因此实例在适用时更好。
+0

感谢您的详细解答,忘记了如果您锁定值类型会发生的拳击。 – 2010-09-29 17:26:01

+0

+1了不起的信息乔恩,希望我最喜欢的答案:-) – Myster 2011-02-18 00:33:43

相关问题