C# ThreadSafeSortedDictionary Implementation with limited methods - c#

I have made an attempt at creating a ThreadSafeSortedDictionary, and I am working with .NET 4.0.
I have had a look at Thread safe SortedDictionary but not confident about the thread safety aspect.
Anyone think this can work, or NOT for a reason I cannot see associated with:
Thread safety - it needs to be thread safe
Performance/Efficiency - I am not majorly concerned about this (unless there is a huge problem with performance)
My code:
public class ThreadSafeSortedDict<TKey, TValue>
{
private readonly SortedDictionary<TKey, TValue> _dict;
private readonly ReaderWriterLockSlim _dictReaderWriterLockSlim;
private readonly ReaderWriterLockSlim _readonlyDictionaryLock;
public Dictionary<TKey, TValue> ReadOnly { get; set; }
public ThreadSafeSortedDict(IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(comparer);
_dictReaderWriterLockSlim = new ReaderWriterLockSlim();
_readonlyDictionaryLock = new ReaderWriterLockSlim();
}
public void Add(TKey key, TValue value)
{
_dictReaderWriterLockSlim.EnterWriteLock();
try
{
_dict.Add(key,value);
}
finally
{
_dictReaderWriterLockSlim.ExitWriteLock();
}
SetReadOnlyDictionary();
}
public void AddRange(IEnumerable<KeyValuePair<TKey,TValue>> keyValues)
{
if (keyValues == null) return;
_dictReaderWriterLockSlim.EnterWriteLock();
try
{
foreach (var keyValue in keyValues)
{
Add(keyValue.Key, keyValue.Value);
}
}
finally
{
_dictReaderWriterLockSlim.ExitWriteLock();
}
SetReadOnlyDictionary();
}
public void Remove(TKey key)
{
_dictReaderWriterLockSlim.EnterWriteLock();
try
{
_dict.Remove(key);
}
finally
{
_dictReaderWriterLockSlim.ExitWriteLock();
}
SetReadOnlyDictionary();
}
public void Replace(IEnumerable<KeyValuePair<TKey, TValue>> newKeyValues)
{
if (newKeyValues == null) return;
_dictReaderWriterLockSlim.EnterWriteLock();
try
{
_dict.Clear();
AddRange(newKeyValues);
}
finally
{
_dictReaderWriterLockSlim.ExitWriteLock();
}
}
private void SetReadOnlyDictionary()
{
_readonlyDictionaryLock.EnterWriteLock();
try
{
ReadOnly = GetSortedKeyValues().ToDictionary(x => x.Key, x => x.Value);
}
finally
{
_readonlyDictionaryLock.ExitWriteLock();
}
}
private List<KeyValuePair<TKey, TValue>> GetSortedKeyValues()
{
_dictReaderWriterLockSlim.EnterReadLock();
try
{
return _dict.ToList();
}
finally
{
_dictReaderWriterLockSlim.ExitReadLock();
}
}
}

I don't see any obvious thread-safety issues with your code. To me, the bigger issues are matters of correctness:
A minor problem is that your ReadOnly property has a public setter. This means any code outside the class can set the property at any time, without any synchronization. Your class design should reflect the intended usage. Presumably you don't want any other code to change the property value, so the setter should be private.
A major problem is that while the class name is ThreadSafeSortedDict<TKey, TValue>, there's no publicly accessible view of the data that is sorted. When you set the ReadOnly property, you create a new unsorted dictionary from the original data. Even though you enumerate the source dictionary in order, which is sorted, there are no guarantees that the data in the new dictionary will remain in the order in which it was added. The dictionary class is, by design, an unordered collection.
If you really want to maintain the data in a SortedDictionary<TKey, TValue> object, and present a sorted view of it, you need to use some type of ordered collection as your ReadOnly property type. This could be another SortedDictionary<TKey, TValue>, but since the intent is for the collection to be read-only, IMHO it makes more sense to make the type IReadOnlyDictionary<TKey, TValue>, returning a ReadOnlyDictionary<TKey, TValue> object. And rather than creating a new object every time the sorted dictionary is modified, you can initialize it lazily and invalidate it when the sorted dictionary is modified.
For example:
public class ThreadSafeSortedDict<TKey, TValue>
{
private readonly SortedDictionary<TKey, TValue> _dict;
private IReadOnlyDictionary<TKey, TValue> _readOnly;
private readonly object _lock = new object();
public IReadOnlyDictionary<TKey, TValue> ReadOnly
{
get
{
lock (_lock)
{
if (_readOnly == null)
{
// NOTE: SortedList is faster when populating from already-sorted data
_readOnly = new ReadOnlyDictionary(new SortedList(_dict));
}
}
return _readOnly;
}
}
public ThreadSafeSortedDict(IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(comparer);
}
public void Add(TKey key, TValue value)
{
lock (_lock)
{
_dict.Add(key,value);
_readOnly = null;
}
}
public void AddRange(IEnumerable<KeyValuePair<TKey,TValue>> keyValues)
{
if (keyValues == null) return;
lock (_lock)
{
foreach (var keyValue in keyValues)
{
Add(keyValue.Key, keyValue.Value);
}
_readOnly = null;
}
}
public void Remove(TKey key)
{
lock (_lock)
{
_dict.Remove(key);
_readOnly = null;
}
}
public void Replace(IEnumerable<KeyValuePair<TKey, TValue>> newKeyValues)
{
if (newKeyValues == null) return;
lock (_lock)
{
_dict.Clear();
AddRange(newKeyValues);
_readOnly = null;
}
}
}
Using lock instead of the reader-writer lock should not affect performance significantly, if at all, and makes the code a lot more obviously thread-safe. It seems to me that the above should be sufficient. Still, if it's not, some improvements you could make include:
Going ahead and using the reader-writer lock. You'll need to acquire the reader lock in the ReadOnly property, and upgrade it to a writer lock if you need to set the _readOnly field (i.e. if it's set to null). This could improve performance if you have a very large amount of contention on the ReadOnly property.
Not bothering with the custom type, and using ConcurrentDictionary<TKey, TValue> instead, just copying its contents and sorting them after the fact when you need a sorted view of the dictionary. Whether this is useful in your case I can't say, as there's not enough context in the question. If you will still need to implement some type of synchronization to avoid trying to retrieve the sorted view when some other thread is trying to modify the collection, then you'll probably want some kind of wrapper type anyway.
Having your type implement IReadOnlyDictionary<TKey, TValue> rather than having a ReadOnly property. Then you can pass your object as a read-only dictionary object where needed, but code that needs to actually modify the dictionary can still do so via the original type.
Given what information is available in the question at the moment, I'd say option #3 is probably best. It would require the most work, but seems to me to provide the cleanest, most efficient API. You would avoid having to have the ReadOnly property and you would avoid having to make a copy of the collection. The class itself would serve as the read-only copy, and it would automatically be kept up-to-date with respect to the underlying dictionary collection, because the implementation of IReadOnlyDictionary<TKey, TValue> would just defer to the underlying dictionary (with synchronization, of course).

First of all, thanks to #Peter Duniho for the information and answer.
I first attempted using reader writer locks, but had quite a bit of trouble and took the advise to use locks for simplicity.
My solution is modifying Peters answer, because I did not want to lock everytime the ReadOnly dictionary was accessed.
And my ultimate goals for this class are:
At any point in time, I would like to access the ReadOnly collection (and stop users to write to it).
If something in the underlying dictionary gets added/removed, I do not want this to affect other's who are already using/reading/iterating the ReadOnly they have access to.
Here is the solution:
public class ThreadSafeSortedDictionary<TKey, TValue>
{
private readonly SortedDictionary<TKey, TValue> _dict;
private readonly object _sync;
public ReadOnlyDictionary<TKey, TValue> ReadOnly { get; private set; }
public IEnumerable<TValue> Values
{
get { return ReadOnly.Values; }
}
public IEnumerable<TKey> Keys
{
get { return ReadOnly.Keys; }
}
public int Count
{
get { return ReadOnly.Count; }
}
public ThreadSafeSortedDictionary(IComparer<TKey> comparer)
{
_dict = new SortedDictionary<TKey, TValue>(comparer);
_sync = new object();
}
public void Add(TKey key, TValue value)
{
lock (_sync)
{
_dict.Add(key, value);
SetReadOnlyDictionary();
}
}
public void AddRange(IEnumerable<KeyValuePair<TKey, TValue>> keyValues)
{
if (keyValues == null) return;
InternalAddRange(keyValues);
}
public void Remove(TKey key)
{
lock (_sync)
{
_dict.Remove(key);
SetReadOnlyDictionary();
}
}
public void Replace(IEnumerable<KeyValuePair<TKey, TValue>> newKeyValues)
{
//Throw exception, because otherwise we'll end up clearing the dictionary
if (newKeyValues == null) throw new ArgumentNullException("newKeyValues");
InternalAddRange(newKeyValues, true);
}
private void InternalAddRange(IEnumerable<KeyValuePair<TKey, TValue>> keyValues, bool clearBeforeAdding = false)
{
lock (_sync)
{
if (clearBeforeAdding) _dict.Clear();
foreach (var kvp in keyValues)
{
_dict.Add(kvp.Key, kvp.Value);
}
SetReadOnlyDictionary();
}
}
private void SetReadOnlyDictionary()
{
lock (_sync)
{
ReadOnly = new DictionaryReadOnly<TKey, TValue>(new SortedList<TKey, TValue>(_dict, _dict.Comparer));
}
}
}
And I used the ReadOnlyDictionary from the answer of Is there a read-only generic dictionary available in .NET?
I used SortedList vs SortedDictionary for the ReadOnly property, because I am creating them quite a few times (during add/remove/replace), and because
1. SortedList perform better in memory
2. are quicker in lookup, and as I am only using them for Read Only purposes, only lookup will be needed.

Related

Concurrent collection supporting random (FIFO) and specific Remove

I'm writing an application which manages a collection that requires frequent enqueuing and dequeuing of items in a miltithreaded environment. With single threaded, a simple List is probably enough, but concurrent nature of the environment poses some issues.
Here's the summary:
The structure needs to have a bool TryAdd(T) method, preferrably Add(TKey, TValue);
The structure needs to have a T TryRemove() method which takes a random or preferrably the first added item (essentially implementing a FIFO queue);
The structure needs to have a bool TryRemove(T) method, preferrably Remove(TKey);
So far I have three ideas, all with their issues:
Implement a class containing a ConcurrentDictionary<TKey, TValue> and a ConcurrentQueue like this:
internal class ConcurrentQueuedDictionary<TKey, TValue> where TKey : notnull
{
ConcurrentDictionary<TKey, TValue> _dictionary;
ConcurrentQueue<TKey> _queue;
object _locker;
public bool TryAdd(TKey key, TValue value)
{
if (!_dictionary.TryAdd(key, value))
return false;
lock (_locker)
_queue.Enqueue(key);
return true;
}
public TValue TryRemove()
{
TKey key;
lock (_locker) {
if (_queue.IsEmpty)
return default(TValue);
_queue.TryDequeue(out key);
}
TValue value;
if (!_dictionary.Remove(key, out value))
throw new Exception();
return value;
}
public bool TryRemove(TKey key)
{
lock (_locker)
{
var copiedList = _queue.ToList();
if (copiedList.Remove(key))
return false;
_queue = new(copiedList);
}
return _dictionary.TryRemove(key, out _);
}
}
but that will require a Lock on Remove(T) because it demands a full deep copy of the initial Queue without the removed item while disallowing read from other threads, which means that at least Remove() will also have this lock, and this is meant to be an operation carried out often;
Implement a class containing a ConcurrentDictionary<TKey, TValue> and a ConcurrentDictionary<int order, TKey>, where order is defined on TryAdd with two properties _addOrder and _removeOrder like this:
internal class ConcurrentQueuedDictionary<TKey, TValue> where TKey : notnull
{
ConcurrentDictionary<TKey, TValue> _dictionary;
ConcurrentDictionary<int, TKey> _order;
int _addOrder = 0;
int _removeOrder = 0;
public bool TryAdd(TKey key, TValue value)
{
if (!_dictionary.TryAdd(key, value))
return false;
if (!_order.TryAdd(unchecked(Interlocked.Increment(ref _addOrder)), key))
throw new Exception(); //Operation faulted, mismatch of data in _order
return true;
}
public TValue TryRemove()
{
TKey key;
if (!(_order.Count > 0 && _order.Remove(unchecked(Interlocked.Increment(ref _removeOrder)), out key)))
return default(TValue);
return _dictionary[key];
}
public bool TryRemove(TKey key)
{
if (!_order.Remove(_order.Where(item => item.Value.Equals(key)).First().Key, out _))
return false;
if (!_dictionary.Remove(key, out _))
throw new Exception();
return true;
}
}
but I'm pretty sure just voicing this implementation had put me on a psychiatric watchlist somewhere because it's gonna be a masochistic nightmare to make work properly;
Straight up locking a List because locks are necessary for option 1 anyway.
Any ideas? I'm kinda stumped by this issue as I don't have the best grasp on concurrent collections. Do I need a custom IProducerConsumerCollection? Is it even possible to have both random (or queued) and specific access to concurrent collection elements? Have any of you faced this before, maybe I'm looking at the issue wrong?
Edit: typos, formatting
Creating a concurrent structure like this by combining built-in concurrent collections should be close to impossible, provided of course that correctness is paramount and race-conditions are strictly forbidden. The good news is that acquiring a lock a few thousands times per second is nowhere near the limit where contention starts to become an issue, provided that the operations inside the protected region are lightweight (their duration is measured in nanoseconds).
One way to achieve O(1) complexity of operations, is to combine a LinkedList<T> and a Dictionary<K,V>:
/// <summary>
/// Represents a thread-safe first in-first out (FIFO) collection of key/value pairs,
/// where the key is unique.
/// </summary>
public class ConcurrentKeyedQueue<TKey, TValue>
{
private readonly LinkedList<KeyValuePair<TKey, TValue>> _queue;
private readonly Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>>
_dictionary;
public ConcurrentKeyedQueue(IEqualityComparer<TKey> comparer = default)
{
_queue = new();
_dictionary = new(comparer);
}
public int Count { get { lock (_queue) return _queue.Count; } }
public bool TryEnqueue(TKey key, TValue value)
{
lock (_queue)
{
ref var node = ref CollectionsMarshal
.GetValueRefOrAddDefault(_dictionary, key, out bool exists);
if (exists) return false;
node = new(new(key, value));
_queue.AddLast(node);
Debug.Assert(_queue.Count == _dictionary.Count);
return true;
}
}
public bool TryDequeue(out TKey key, out TValue value)
{
lock (_queue)
{
if (_queue.Count == 0) { key = default; value = default; return false; }
var node = _queue.First;
(key, value) = node.Value;
_queue.RemoveFirst();
bool removed = _dictionary.Remove(key);
Debug.Assert(removed);
Debug.Assert(_queue.Count == _dictionary.Count);
return true;
}
}
public bool TryTake(TKey key, out TValue value)
{
lock (_queue)
{
bool removed = _dictionary.Remove(key, out var node);
if (!removed) { value = default; return false; }
_queue.Remove(node);
(_, value) = node.Value;
Debug.Assert(_queue.Count == _dictionary.Count);
return true;
}
}
public KeyValuePair<TKey, TValue>[] ToArray()
{
lock (_queue) return _queue.ToArray();
}
}
This combination is also used for creating LRU caches.
You can measure the lock contention in your own environment under load, by using the Monitor.LockContentionCount property: "Gets the number of times there was contention when trying to take the monitor's lock." If you see the delta per second to be a single digit number, there is nothing to worry about.
For a version that doesn't use the CollectionsMarshal.GetValueRefOrAddDefault method, and so it can be used on .NET versions older than .NET 6, see the first revision of this answer.

Best way to implement consumer queue that you can remove items from sequentially (.net 6)

new poster here so I hope this makes sense ...
I need to create a collection that I can remove items from in sequence (basically stock market time series data).
The data producer is multi-threaded and doesn't guarantee that the data will come in sequence.
I've looked all around for a solution but the only thing I can come up with is to create my own custom dictionary, using ConcurrentDictionary and implementing the IProducerConsumer interface so it can be used with with BlockingCollection.
The code I have below does work, but produces an error
System.InvalidOperationException: The underlying collection was
modified from outside of the BlockingCollection
when using the GetConsumingEnumerable() for loop, and the next key in the sequence is not present in the dictionary. In this instance I would like to wait for a specified amount of time
and then attempt to take the item from the queue again.
My questions is:
What's the best way to handle the error when there is no key present. At the moment it seems handling the error would require exiting the loop. Perhaps using GetConsumingEnumerable() is not the right way to consume and a while loop would work better?
Code is below - any help/ideas much appreciated.
IProducerConsumer implementation:
public abstract class BlockingDictionary<TKey, TValue> : IProducerConsumerCollection<KeyValuePair<TKey, TValue>> where TKey : notnull
{
protected ConcurrentDictionary<TKey, TValue> _dictionary = new ConcurrentDictionary<TKey, TValue>();
int ICollection.Count => _dictionary.Count;
bool ICollection.IsSynchronized => false;
object ICollection.SyncRoot => throw new NotSupportedException();
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
_dictionary.ToList().CopyTo(array, index);
}
void ICollection.CopyTo(Array array, int index)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
((ICollection)_dictionary.ToList()).CopyTo(array, index);
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return ((IEnumerable<KeyValuePair<TKey, TValue>>)_dictionary).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<KeyValuePair<TKey, TValue>>)this).GetEnumerator();
}
public KeyValuePair<TKey, TValue>[] ToArray()
{
return _dictionary.ToList().ToArray();
}
bool IProducerConsumerCollection<KeyValuePair<TKey, TValue>>.TryAdd(KeyValuePair<TKey, TValue> item)
{
return _dictionary.TryAdd(item.Key, item.Value);
}
public virtual bool TryTake(out KeyValuePair<TKey, TValue> item)
{
item = this.FirstOrDefault();
TValue? value;
return _dictionary.TryRemove(item.Key, out value);
}
}
Time Sequence queue implementation (inherits above)
public class TimeSequenceQueue<T> : BlockingDictionary<DateTime, T>
{
private DateTime _previousTime;
private DateTime _nextTime;
private readonly int _intervalSeconds;
public TimeSequenceQueue(DateTime startTime, int intervalSeconds)
{
_intervalSeconds = intervalSeconds;
_previousTime = startTime;
_nextTime = startTime;
}
public override bool TryTake([MaybeNullWhen(false)] out KeyValuePair<DateTime, T> item)
{
item = _dictionary.SingleOrDefault(x => x.Key == _nextTime);
T? value = default(T);
if (item.Value == null)
return false;
bool result = _dictionary.TryRemove(item.Key, out value);
if (result)
{
_previousTime = _nextTime;
_nextTime = _nextTime.AddSeconds(_intervalSeconds);
}
return result;
}
}
Usage:
BlockingCollection<KeyValuePair<DateTime, object>> _queue = new BlockingCollection<KeyValuePair<DateTime, object>>(new TimeSequenceQueue<object>());
Consuming loop - started in new thread:
foreach (var item in _queue.GetConsumingEnumerable())
{
// feed downstream
}
When using the GetConsumingEnumerable() for loop, and the next key in the sequence is not present in the dictionary [...] I would like to wait for a specified amount of time and then attempt to take the item from the queue again.
I will try to answer this question generally, without paying too much attention to the specifics of your problem. So let's say that you are consuming
a BlockingCollection<T> like this:
foreach (var item in collection.GetConsumingEnumerable())
{
// Do something with the consumed item.
}
...and you want to avoid waiting indefinitely for an item to arrive. You want to wake up every 5 seconds and do something, before waiting/sleeping again.
Here is how you could do it:
while (!collection.IsCompleted)
{
bool consumed = collection.TryTake(out var item, TimeSpan.FromSeconds(5));
if (consumed)
{
// Do something with the consumed item.
}
else
{
// Do something before trying again to take an item.
}
}
The above pattern imitates the actual source code of the BlockingCollection<T>.GetConsumingEnumerable method.
If you want to get fancy you could incorporate this functionality in a custom extension method for the BlockingCollection<T> class, like this:
public static IEnumerable<(bool Consumed, T Item)> GetConsumingEnumerable<T>(
this BlockingCollection<T> source, TimeSpan timeout)
{
while (!source.IsCompleted)
{
bool consumed = source.TryTake(out var item, timeout);
yield return (consumed, item);
}
}
Usage example:
foreach (var (consumed, item) in collection.GetConsumingEnumerable(
TimeSpan.FromSeconds(5)))
{
// Do something depending on whether an item was consumed or not.
}

Windows Phone 7 - OrderedDictionary / alternatives

I'm completely new to C#, so I'm about to make a horrible attempt at my own version of an OrderedDictionary unless someone can suggest an alternative.
I need to be able to access my elements by array index, retaining the order they were added, and I also will be frequently updating individual elements using their key.
Is there a collection that allows this on the phone?
If I keep a List and Dictionary will they both be pointing to the same item or is there some kind of pointer thing I have to do?:
Item i = new Item();
list.Add(i);
dict.Add("key", i);
Here's my implementation (comes from the open source OpenNETCF Extensions library):
public class OrderedDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
private Dictionary<TKey, TValue> m_dictionary;
private List<TValue> m_list = new List<TValue>();
private object m_syncRoot = new object();
public OrderedDictionary()
{
m_dictionary = new Dictionary<TKey, TValue>();
}
public OrderedDictionary(IEqualityComparer<TKey> comparer)
{
m_dictionary = new Dictionary<TKey, TValue>(comparer);
}
public void Add(TKey key, TValue value)
{
lock (m_syncRoot)
{
m_dictionary.Add(key, value);
m_list.Add(value);
}
}
public TValue this[int index]
{
get { return m_list[index]; }
}
public TValue this[TKey key]
{
get { return m_dictionary[key]; }
}
public int Count
{
get { return m_dictionary.Count; }
}
public Dictionary<TKey, TValue>.KeyCollection Keys
{
get { return m_dictionary.Keys; }
}
public Dictionary<TKey, TValue>.ValueCollection Values
{
get { return m_dictionary.Values; }
}
public void Clear()
{
lock (m_syncRoot)
{
m_dictionary.Clear();
m_list.Clear();
}
}
public bool ContainsKey(TKey key)
{
return m_dictionary.ContainsKey(key);
}
public bool ContainsValue(TValue value)
{
return m_dictionary.ContainsValue(value);
}
public void Insert(int index, TKey key, TValue value)
{
lock (m_syncRoot)
{
m_list.Insert(index, value);
m_dictionary.Add(key, value);
}
}
public void Remove(TKey key)
{
lock (m_syncRoot)
{
if (ContainsKey(key))
{
var existing = m_dictionary[key];
m_list.Remove(existing);
m_dictionary.Remove(key);
}
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Using a List and a Dictionary is probably a good option actually. The "pointer thing" that you're talking about happens by default for Objects in .NET (any class and/or structure). All objects in .NET are passed around by reference.
So, if you use:
Item i = new Item();
list.Add(i);
dict.Add("key",i);
Console.WriteLine(list.Last() == dict["key"]);
Your output will be "true".
Best of luck!
I won't suggest using OrderedDictionary, since it's a non-generic container.
However, if you just want to use it like always. You can port Mono's version of OrderedDictionary.
https://github.com/mono/mono/blob/master/mcs/class/System/System.Collections.Specialized/OrderedDictionary.cs
Here's some tips if you want to port this:
Remove any unavailable interface
Remove serialization-related code
Replace ArrayList with List<object>
Replace Hashtable with Dictionary<object, object>

What should I use as an alternative to System.Collections.Concurrent.ConcurrentDictionary (3.5)

I require a thread safe collection but are unable to use ConcurrentDictionary as its .NET 4.0, and I need to use .NET 3.5. What are alternatives are out there?
In the end I found what I was after. A nuget package of the TPL backported to 3.5. It can be found here
http://nuget.org/packages/TaskParallelLibrary
Have a look at this article
“Thread safe” Dictionary(TKey,TValue)
For Framework 3.5 I see the following options:
Dictionary + Monitor. Simple wrap to lock
Dictionary + ReaderWriterLock. Better than the previous one, because has got read and write locks. So several threads can read, and just one - write.
Dictionary + ReaderWriterLockSlim. It's just optimisation of the previous one.
Hashtable. From my experience, it's the slowest method. Check Hashtable.Synchronized() method, it's a ready to go solution from Microsoft.
I would use Dictionary + ReaderWriterLock or ReaderWriterLockSlim.
You can create a simple wrapper and only implement what you need. Keep in mind that it is not thread safe to perform an operation on a Dictionary that relies on the result of a previous call. This is exemplified in the TryAdd method.
class ConcurrentMap<K, V>
{
readonly Dictionary<K, V> _map = new Dictionary<K, V>();
public bool TryGetValue(K key, out V value)
{
lock (_map)
{
return _map.TryGetValue(key, out value);
}
}
public bool TryAdd(K key, V value)
{
lock (_map)
{
if (!_map.ContainsKey(key))
{
_map.Add(key, value);
return true;
}
return false;
}
}
public bool TryRemove(K key)
{
lock (_map)
{
return _map.Remove(key);
}
}
}
I just needed that for .Net 3.5... someone's still there back :)
adding my class, added some more functionality than previous attached codes.
public class SafeDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, TValue>>
{
private readonly object _Padlock = new object();
private readonly Dictionary<TKey, TValue> _Dictionary = new Dictionary<TKey, TValue>();
public TValue this[TKey key]
{
get
{
lock (_Padlock)
{
return _Dictionary[key];
}
}
set
{
lock (_Padlock)
{
_Dictionary[key] = value;
}
}
}
public bool TryGetValue(TKey key, out TValue value)
{
lock (_Padlock)
return _Dictionary.TryGetValue(key, out value);
}
public bool TryAdd(TKey key, TValue value)
{
lock (_Padlock)
{
if (!_Dictionary.ContainsKey(key))
{
_Dictionary.Add(key, value);
return true;
}
return false;
}
}
public bool TryRemove(TKey key)
{
lock (_Padlock)
{
return _dictionary.Remove(key);
}
}
}
internal void Add(TKey key, TValue val)
{
lock (_Padlock)
{
_Dictionary.Add(key, val);
}
}
public bool ContainsKey(TKey id)
{
lock (_Padlock)
return _Dictionary.ContainsKey(id);
}
public List<KeyValuePair<TKey, TValue>> OrderBy(Func<KeyValuePair<TKey, TValue>, TKey> func)
{
lock (_Padlock)
return _Dictionary.OrderBy(func).ToList();
}
public Dictionary<TKey, TValue>.ValueCollection Values
{
get
{
lock (_Padlock)
return _Dictionary.Values;
}
}
public Dictionary<TKey, TValue>.KeyCollection Keys
{
get
{
lock (_Padlock)
return _Dictionary.Keys;
}
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
lock (_Padlock)
return _Dictionary.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
lock (_Padlock)
return _Dictionary.GetEnumerator();
}
}

C# reload singleton cache

I need some directions here.
I have the following key/value cache:
public class Cache<TKey, TValue> : ICache<TKey, TValue>
{
private readonly IDictionary<TKey, TValue> _internalCache;
private readonly object _syncLock = new object();
public Cache()
{
_internalCache = new Dictionary<TKey, TValue>();
}
public TValue this[TKey key]
{
get
{
lock (_syncLock) {
//...
}
}
set
{
lock (_syncLock) {
//...
}
}
}
public ICollection<TValue> GetAll()
{
lock (_syncLock) {
return _internalCache.Values;
}
}
public bool ContainsKey(TKey key)
{
lock (_syncLock)
{
return _internalCache.ContainsKey(key);
}
}
}
The cache above is used by a singleton wrapper:
public class ActivityCache : ICache<string, Activity>
{
private readonly ICache<string, Activity> _cache = new Cache<string, Activity>();
private static readonly ActivityCache _instance = new ActivityCache();
// http://www.yoda.arachsys.com/csharp/singleton.html
static ActivityCache()
{
}
ActivityCache()
{
}
public static ActivityCache Instance
{
get { return _instance; }
}
public Activity this[string activityUrl]
{
get
{
if (string.IsNullOrEmpty(activityUrl))
{
return null;
}
return _cache[activityUrl];
}
set
{
if (string.IsNullOrEmpty(activityUrl))
{
return;
}
_cache[activityUrl] = value;
}
}
public ICollection<Activity> GetAll()
{
return _cache.GetAll();
}
public bool ContainsKey(string key)
{
return _cache.ContainsKey(key);
}
}
This is working fine (I haven't noticed/heard of any errors... yet :) ).
But now I have a problem. I need to reload the cache with new key/values.
Question 1.) Can I implement a "safe" reload method that reloads the cache (the Dictionary in the Cache class) ?
E.g:
public void Reload(IDictionary<TKey, TValue> values)
{
lock (_syncLock)
{
_internalCache.Clear();
foreach (KeyValuePair<TKey, TValue> value in values)
{
/* Problems can (will) occur if another
thread is calling the GetAll method... */
_internalCache[value.Key] = value.Value;
}
}
}
Question 2.) Should I use some IoC container or some other library instead?
Thanks!
Note: I'm using .NET 3.5
use ConcurrentDictionary, then you wont have to deal with Synchronization.
Also, you dont want to reload all the cache items as well. instead you want to do staggering, load the cache objects in the key/value store on demand.
You can use timestamp or some versioning for that. if you reload the data per each key/value pair then you wont have to lock the whole collection.
I really recommend you to use ConcurrentDictionary.
You should lock around GetAll as well.
You could use a double buffer type technique to make the reload less painful :
public void Reload(IDictionary<TKey, TValue> values)
{
cache = new Dictionary<TKey, TValue> ();
foreach (KeyValuePair<TKey, TValue> value in values)
{
cache[value.Key] = value.Value;
}
lock (_syncLock)
{
_internalCache = cache;
}
}
This will work providing you don't mind readers accessing potentially out of date information whilst you call reload.

Categories

Resources