Reputation: 6136
The code below is going to be called from a website so having a static dictionary object in a non static class needs to be threadsafe. Basically the purpose of the code is to encapsulate logic and to maintain the life-time of the perfmon counter(s), which are stored in an instance of CounterContainer. The constructor is called passing in instanceName. The constructor needs to check to see if a CounterContainer for that instanceName has been defined and stored in the dictionary. If so, it can (and must) use that instance. If not it creates an instance of the CounterContainer, stores it in the dictionary and then uses that instance. The instance of the CounterContainer to be used is stored in a non static member so is thread safe from that point on.
As the only place that that the static dictionary is used is within the constructor it feels to me that it will be safe to lock on the dictionary for the duration that it will be accessed? Is this going to cause any unforeseen issues later on with anything like blocking / deadlocks? I can't see anything, but haven't really had the need to consider this sort of thing too much in the past.
I have also considered lock(this): but I don't think that would work as it would only lock the instance of PerformanceCounters being created, and not the underlying static dictionary (so would not be threadsafe).
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();
}
}
}
Upvotes: 1
Views: 403
Reputation: 113292
Not to disagree with the suggestion that ConcurrentDictionary be used, but to answer more generally:
this
because something external to your that code could lock on the object and you can easily get a deadlock or at least lock contention that can only be found by looking at both the code internal to the class, and the calling code (and the person writing one may not have access to the other). For similar reasons, never lock on a Type
.partial
with such code, and if you do use partial
, try to keep all locks on an object in the same place. No technical reason here, but it does help when you need to think about all the potential places a lock could be taken.Upvotes: 5
Reputation: 52430
Yes, _containers is theoretically ok as it is scoped the same as the dictionary instance, that is to say, it's a static (obviously - it IS the dictionary instance.)
However once thing worries about your overall design is that you say this is being hosted in a website. Each asp.net worker is a separate process so your static dictionary may be destroyed when IIS recycles the worker process due to idling or otherwise. Also, you may have more than one instance of your dictionary if you are using web gardens (multiple workers per application.)
Upvotes: 0
Reputation: 46008
Yes, it will work as _containers is static.
You might want to take a look at ReaderWriterLockSlim as well so you want block the theard so much (improvement to performance)
Upvotes: 2
Reputation: 22849
It seems to me quite common to have an explicit object instance that is used exclusively for locking.
private readonly object containerLock = new object();
...
lock (containerLock) {
...
}
Another tip: If you are working against .NET 4, consider using ConcurrentDictionary
.
Upvotes: 0