For the past eight(8) years Schalk Neethling has been working as a freelance developer under the pseudo of Volume4 and is now the president of Overt Strategy Consulting. During this period he has completed over 300 projects ranging from full web application development to complete branding. As president and lead developer of Overt Strategy Consulting, Schalk Neethling and his team has released a 100% Java standards based content management system called AlliedBridge and business document exchange and review system, called Doc-Central. Schalk Neethling is also actively involved on a daily basis in the open source, web standards and accessibility areas and is a current active member of the Web Standards Group. Schalk is also the co-founder and president of the non-profit The South Web Standards and Accessibility Group, which aims to actively educate and raise awareness of web standards and accessibility to both the developer society as well as business large and small. Schalk also has a long relationship with DZone and is currently zone leader for both the web builder, css.dzone.com, as well as the .NET zone, dotnet.dzone.com, and you can find a lot of his writing there as well as on his blog located at schalkneethling.alliedbridge.com. Schalk is constantly expanding on his knowledge of various aspects of technology and loves to stay in touch with the latest happenings. For Schalk web development and the internet is not just a job, it is a love, a passion and a life style. Schalk has posted 173 posts at DZone. View Full User Profile

.NET Synchronised Dictionary

06.24.2008
| 6904 views |
  • submit to reddit

In .NET 2.0 and above the SyncRoot was removed from the Dictionary and Hastable classes which meant you can't create thread-safe versions of these anymore. This was done (properly in my reasoning) by the .NET team to move synchronisation to be the responsibility of the developer as synchronisation usually happens at a higher level and could involve multiple resources which needed to be synchronised together.

Now saying all that, there are places where a simple thread safe dictionary or list class is just a necessity and it's a complete pain having to develop your our synchronisation around it each time.

So here I provide a simple implementation of a generic synchronised dictionary class that is thread-safe, this was developed in .NET 3.5 using the new Threading.ReaderWriterLockSlim class.

Public Class SynchronisedDictionary(Of KEY_TYPE, ITEM_TYPE)
Implements IDictionary(Of KEY_TYPE, ITEM_TYPE)
Private _rwLock As New Threading.ReaderWriterLockSlim
Private _dict As New Generic.Dictionary(Of KEY_TYPE, ITEM_TYPE)

Private Class AcquireWriteLock
Implements IDisposable
Private _rwLock As New Threading.ReaderWriterLockSlim
Public Sub New(ByVal rwLock As Threading.ReaderWriterLockSlim)
_rwLock = rwLock
_rwLock.EnterWriteLock()
End Sub
Private disposedValue As Boolean = False ' To detect redundant calls

' IDisposable
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: free other state (managed objects).
_rwLock.ExitWriteLock()
End If
' TODO: free your own state (unmanaged objects).
' TODO: set large fields to null.
End If
Me.disposedValue = True
End Sub
#Region " IDisposable Support "
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Private Class AcquireReadLock
Implements IDisposable
Private _rwLock As New Threading.ReaderWriterLockSlim
Public Sub New(ByVal rwLock As Threading.ReaderWriterLockSlim)
_rwLock = rwLock
_rwLock.EnterReadLock()
End Sub
Private disposedValue As Boolean = False ' To detect redundant calls

' IDisposable
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: free other state (managed objects).
_rwLock.ExitReadLock()
End If
' TODO: free your own state (unmanaged objects).
' TODO: set large fields to null.
End If
Me.disposedValue = True
End Sub
#Region " IDisposable Support "
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Public Sub Add(ByVal item As System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)).Add
Using New AcquireWriteLock(_rwLock)
_dict(item.Key) = item.Value
End Using
End Sub
Public Sub Clear() Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)).Clear
Using New AcquireWriteLock(_rwLock)
_dict.Clear()
End Using
End Sub
Public Function Contains(ByVal item As System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)).Contains
Using New AcquireReadLock(_rwLock)
Return _dict.Contains(item)
End Using
End Function

Public Sub CopyTo(ByVal array() As System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE), ByVal arrayIndex As Integer) Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)).CopyTo
Using New AcquireReadLock(_rwLock)
_dict.ToArray.CopyTo(array, arrayIndex)
End Using
End Sub

Public ReadOnly Property Count() As Integer Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)).Count
Get
Using New AcquireReadLock(_rwLock)
Return _dict.Count
End Using
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)).IsReadOnly
Get
Return False
End Get
End Property
Public Function Remove(ByVal item As System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)) As Boolean Implements System.Collections.Generic.ICollection(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)).Remove
Using New AcquireWriteLock(_rwLock)
Return _dict.Remove(item.Key)
End Using
End Function

Public Sub Add(ByVal key As KEY_TYPE, ByVal value As ITEM_TYPE) Implements System.Collections.Generic.IDictionary(Of KEY_TYPE, ITEM_TYPE).Add
Using New AcquireWriteLock(_rwLock)
_dict(key) = value
End Using
End Sub

Public Function ContainsKey(ByVal key As KEY_TYPE) As Boolean Implements System.Collections.Generic.IDictionary(Of KEY_TYPE, ITEM_TYPE).ContainsKey
Using New AcquireReadLock(_rwLock)
Return _dict.ContainsKey(key)
End Using
End Function
Default Public Property Item(ByVal key As KEY_TYPE) As ITEM_TYPE Implements System.Collections.Generic.IDictionary(Of KEY_TYPE, ITEM_TYPE).Item
Get
Using New AcquireReadLock(_rwLock)
Return _dict(key)
End Using
End Get
Set(ByVal value As ITEM_TYPE)
Using New AcquireWriteLock(_rwLock)
_dict(key) = value
End Using
End Set
End Property
Public ReadOnly Property Keys() As System.Collections.Generic.ICollection(Of KEY_TYPE) Implements System.Collections.Generic.IDictionary(Of KEY_TYPE, ITEM_TYPE).Keys
Get
Using New AcquireReadLock(_rwLock)
Return _dict.Keys
End Using
End Get
End Property
Public Function Remove(ByVal key As KEY_TYPE) As Boolean Implements System.Collections.Generic.IDictionary(Of KEY_TYPE, ITEM_TYPE).Remove
Using New AcquireWriteLock(_rwLock)
_dict.Remove(key)
End Using
End Function

Public Function TryGetValue(ByVal key As KEY_TYPE, ByRef value As ITEM_TYPE) As Boolean Implements System.Collections.Generic.IDictionary(Of KEY_TYPE, ITEM_TYPE).TryGetValue
Using New AcquireReadLock(_rwLock)
Return _dict.TryGetValue(key, value)
End Using
End Function

Public ReadOnly Property Values() As System.Collections.Generic.ICollection(Of ITEM_TYPE) Implements System.Collections.Generic.IDictionary(Of KEY_TYPE, ITEM_TYPE).Values
Get
Using New AcquireReadLock(_rwLock)
Return _dict.Values
End Using
End Get
End Property
Public Function GetEnumeratorGeneric() As System.Collections.Generic.IEnumerator(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)) Implements System.Collections.Generic.IEnumerable(Of System.Collections.Generic.KeyValuePair(Of KEY_TYPE, ITEM_TYPE)).GetEnumerator
Using New AcquireReadLock(_rwLock)
Return _dict.GetEnumerator
End Using
End Function
Public Function GetEnumerator() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
Using New AcquireReadLock(_rwLock)
Return _dict.GetEnumerator
End Using
End Function
End Class
Original Author

Original article written by Noel Lysaght

0
Your rating: None
Published at DZone with permission of its author, Schalk Neethling.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)