Faster TMultiReadExclusiveWriteSynchronizer?
TOmniMREW
from OmniThreadLibrary
claims to be faster and more lightweight:
OTL is an excellent threading lib, BTW.
Sample Code
TOmniReaderWriterLock = class(TInterfacedObject, IReaderWriterLock)private omrewReference: Integer;public { IReaderWriterLock } procedure BeginRead; procedure EndRead; procedure BeginWrite; procedure EndWrite;end;{ TOmniReaderWriterLock }procedure TOmniReaderWriterLock.BeginRead;var currentReference: Integer;begin //Wait on writer to reset write flag so Reference.Bit0 must be 0 than increase Reference repeat currentReference := Integer(omrewReference) and not 1; until currentReference = Integer(InterlockedCompareExchange(Pointer(omrewReference), Pointer(Integer(currentReference) + 2), Pointer(currentReference)));end;procedure TOmniReaderWriterLock.EndRead;begin //Decrease omrewReference InterlockedExchangeAdd(@omrewReference, -2);end;procedure TOmniReaderWriterLock.BeginWrite;var currentReference: integer;begin //Wait on writer to reset write flag so omrewReference.Bit0 must be 0 then set omrewReference.Bit0 repeat currentReference := omrewReference and (not 1); until currentReference = Integer(InterlockedCompareExchange(Pointer(omrewReference), Pointer(currentReference+1), Pointer(currentReference))); //Now wait on all readers repeat until omrewReference = 1;end;procedure TOmniReaderWriterLock.EndWrite;begin omrewReference := 0;end;
In the end i used a compromise solution. The Omni
reader-writer lock uses "slim" principles (spinning bit-manipulations). Like Window's own, it doesn't support lock escalation. I've tested it, and it doesn't seem to lockup crash or deadlock.
In the end i used a fallback situation. The most generic of generic interfaces to support "read-write" concepts:
IReaderWriterLock = interface ['{6C4150D0-7B13-446D-9D8E-866B66723320}'] procedure BeginRead; procedure EndRead; procedure BeginWrite; procedure EndWrite;end;
And then we decide at runtime which implementation to use. If we're on Windows Vista or new, use Window's own SlimReaderWriter
, otherwise fallback to Omni
version:
TReaderWriterLock = class(TObject)public class function Create: IReaderWriterLock;end;class function TReaderWriterLock.Create: IReaderWriterLock;begin if Win32MajorVersion >= 6 then //SRWLocks were introduced with Vista/Server 2008 (Windows version 6.0) begin //Use the Windows built-in Slim ReaderWriter lock Result := TSlimReaderWriterLock.Create; end else begin //XP and earlier fallback to Omni equivalent Result := TOmniReaderWriterLock.Create; end;end;
Note: Any code is released into the public domain. No attribution required.
The Delphi TMultiReadExclusiveWriteSynchronizer
is very sophisticated - it can be acquired recursively and you can update from Read
to Write
.
This comes with a cost, which in this case means managing a bucket of shared state per thread. As the Windows thread-local mechanics (accessible via threadvar
) is too simplistic for this (not able cope with several MREWS instances) it is done in a rather inefficient way – see the RTL or JCL sources – the implementations are quite similar, sharing bad performance and update-deadlock risk.
First ensure you really need the MREWS functionality – I assume, according to proportional size of locking overhead to the workload, you will be much better off with a TCriticalSection
.
If you really-really need it, go with the Delphi implementation and watch out for the possible hidden unlock in BeginWrite
– see it's documentation and return value meaning.
It is possible to implement a Vista-like SRW
using the Interlocked
functions or inline assembly, but it's not worth the effort in most cases.