Bringing simplicity to a multi-threaded world
This Read-Writer Locker is designed so that upgrading from read locks to write locks are intuitive, highly nested locking is encouraged, and deadlocks cannot happen.
Forked from jpmikkers' ReaderWriterLockAlt located at
http://readerwriterlockalt.codeplex.com/. As such, this also supports .NET 2.0+. If you would like to learn how to design your own, or you would like to help with this project, please start there.
Don't forget to click the block headers (Problem Solved, Example, License, and Additional Information). They are links to full articles about their topic.
Features
- No deadlocks when dealing with complex locking.
- No manual upgrade locks. Upgrading a lock is as simple as nesting a write lock inside a read lock.
- Compatibility with ReaderWriterLock and ReaderWriterLockSlim through similarly named wrapper classes.
- Aborting a thread or forgetting to dispose the locker in the middle of thread locking shouldn't cause stability issues (work-in-progress).
- Portability. The ideas behind this locking mechanism can be applied to many different languages and platforms (Java and C++ not finished).
Problem SolvedThe typical lock(A) locking on lock(B), and lock(B) locking on lock(A). I detect when it happens, and let one thread "run its course" to solve this issue.
ReadWriteLocker Lock = New ReadWriteLocker(True);
using (IDisposable ReadLock = Lock.GetReadLock())
{
// Perform any reading you might need, like iterating through a List.
using (IDisposable WriteLock = Lock.GetWriteLock())
{
// Perform any writing you may need, like adding items to a List.
} // End using
} // End using
Imports NoHardLockReadWriteLocker
Imports ThreadSafeClasses
Public Module MainProgram
Public Sub main()
InitializeThreads()
End Sub
// List which has thread safety turned on, and initialized to an array of integers.
Dim List As New ThreadSafeList(Of Integer)(True, New Integer {0,5,0,4,0,3,0,2,0,1})
Dim IsBusy As Integer = 0
Public Sub InitalizeThreads()
For Index As Integer = 0 To 10
Interlocked.Increment(IsBusy)
ThreadPool.QueueUserWorkItem(FunctionToRunInMultipleThreads, Index)
Next
While IsBusy > 0
Thread.Sleep(500)
End While
End Sub
Public Sub FunctionToRunInMultipleThreads(threadContext As Object)
Dim threadIndex As Integer = CInt(threadContext)
Console.WriteLine("thread {0} started...", threadIndex);
' Thread-safety does not need to be turned on for just the modifying while enumerating.
For Each Item As Integer In List
' This normally results in a enumeration error!
List.Add(Item)
Console.WriteLine("Value {0} found...note that items added in for-each won't appear yet.", Item);
Next
For Each Item As Integer In List
Console.WriteLine("Value {0} found...note that items added in previous for-each appear!", Item);
Next
' This is not thread-safe! This needs a read lock wrapped around it.
While (List.Count > 17)
' This can throw an error if List.Count is grabbed, an item is removed
' via another thread. Then RemoveAt is called. That position would be gone.
List.RemoveAt(List.Count)
End While
' This is thread-safe. Notice the read lock wrapped around it.
'
' But this will loop forever, because removing an item will be cached
' and the count will never decrease.
Using ReadLock As IDisposable = List.GetReadLock()
While (List.Count > 14)
List.RemoveAt(List.Count)
End While
End Using
' This is correctly thread-safe.
While (List.Count > 10)
Using ReadLock As IDisposable = List.GetReadLock()
List.RemoveAt(List.Count)
End Using
End While
Interlocked.Decrement(IsBusy)
End Sub
End Module
License
This project uses a new Tri-License Choice MIT/L-GPL/MS-PL. As such, you have a choice of choosing MIT, L-GPL v2 or v3, or MS-PL. Or you can choose to use the tri-license and let others have their choice. While using this tri-license, MIT will be used in regards to the code, and L-GPL v2 for patents (so algorithms remain open source). Only when MS-PL is chosen, does it apply (dropping MIT and L-GPL).
Additional Information
If you would like regular locking (i.e. SyncLock/lock) that cannot be dead-locked, simply use GetSyncLock() or GetLock() according to your programming language/preference.