Reputation: 4676
I have multiple queues that are being accessed by multiple threads. To achieve thread-safety, I did the following:
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;
}
I could also lock MyQueues
, but then I would lock more than necessary. So my question is, if locking an object contained in a dictionary is going to work - assuming that a key's value (the queue) is never changed.
Upvotes: 7
Views: 3484
Reputation: 241789
So my question is, if locking an object contained in a dictionary is going to work - assuming that a key's value (the queue) is never changed.
Looking at your code here:
lock (queue) {
if (queue.Count() > 0) {
res = queue.Dequeue();
}
}
You can, but I would not do this. You should never lock on the object itself, as you might be competing with other threads of code that will lock on the same object, including Queue<T>
itself (who could lock on this
).
So, at a minimum, you should create a dedicated lock object for each queue.
But, is there a reason that you aren't using ConcurrentQueue<T>
? That'd be the simplest solution, and moves the burden of getting it right to the Framework.
Upvotes: 1
Reputation: 1064184
The fact that it is an element in a dictionary is largely irrelevant - as long as it is a reference type (which Queue<string>
is) - then each queue, when fetched from the dictionary, is going to be the same object
instance each time. This means it will work perfectly reasonably with locking at the per-queue level.
So basically: yes, that should work fine - as long as the Enqueue
does the same per-queue locking. As Jon notes - whether you should do that is another question.
Personally, I'm still of the opinion that Monitor
should have been a non-static type, and that you could only lock Monitor
instances, rather than any object
.
Upvotes: 2
Reputation: 1503799
You can - but I generally wouldn't. Personally I usually attempt to lock on plain System.Object
instances which aren't used for anything else, and ideally aren't even exposed to any code other than the class locking on them. That way you can be absolutely sure that nothing else is going to lock.
In this case it looks like you've got control over the queues so you know they won't be used by other code, but it's possible that the code inside Queue<T>
will lock on this
. That's probably not the case, but it's the kind of thing I would worry about.
Fundamentally, I wish that .NET hadn't taken Java's approach of "a monitor for every object" - I wish Monitor
had been an instantiable class.
(I assume you're only actually reading from the dictionary from multiple threads? It's not safe to use dictionaries for multi-threaded read/write.)
Upvotes: 7