那里有{em>更快种TMultiReadExclusiveWriteSynchronizer
吗? FastCode也许?
从Windows Vista开始,Microsoft添加了Slim Reader/Writer lock。它performs much better比Delphi的TMultiReadExclusiveWriteSynchronizer
。不幸的是,它只存在于Windows Vista及更高版本中,这是很少有客户真正拥有的东西。
大概在Slim Reader/Writer lock
中使用的概念可以在本机Delphi代码中重做 - 但有人做过吗?
我的情况是获取和释放TMultiReadExclusiveWriteSynchronizer
上的锁(即使没有争用 - 单个线程),导致100%的开销(操作时间加倍)。我可以在没有锁定的情况下运行,但之后我的班级不再是线程安全的。
是否有更快的TMultiReadExclusiveWriteSynchronizer
?
注意:如果我使用TCriticalSection
我只会遭受2%的性能下降(虽然当获取成功时,已知关键部分会很快,即,虽然它是单线程的,但没有争用)。 CS的缺点是我失去了“多个读者”的能力。
在TMultiReadExclusiveWriteSynchronizer
和BeginRead
内使用EndRead
花费了相当多的时间:
然后移植代码以使用Window自己的 SlimReaderWriter Lock (其中一些代码重写,因为它不支持递归锁定),并分析了resutls:
TMultiReadExclusiveWriteSynchronizer
:每次迭代10,698 ns
10,697,772,613 ns迭代1,000,000次
SRWLock
:每次迭代8,802 ns
8,801,678,339 ns迭代1,000,000次
Omni Reader-Writer lock
:每次迭代8,941 ns
8,940,552,487 ns迭代1,000,000次
使用SRWLocks(又名Omni的旋转锁定)时,性能提高了17%。
现在,我无法将代码永久地转换为使用Windows Vista SRWLocks,因为仍有一些客户仍在Windows XP上。
Slim锁只是小心使用InterlockedCompareExchange
函数;但比我能成功使用更加小心。我这个远离窃取所涉及的140台机器指令,并完成它。
答案 0 :(得分:6)
OmniThreadLibrary
的 TOmniMREW
声称更快,更轻量级:
OTL是一个优秀的线程库,BTW。
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;
答案 1 :(得分:3)
最后,我使用了妥协解决方案。 Omni
读写器锁使用“slim”原则(旋转位操作)。像Window自己一样,它不支持锁升级。我已经测试了它,似乎 lockup 崩溃或死锁。
最后我使用了后备情况。支持“读写”概念的最通用的通用接口:
IReaderWriterLock = interface
['{6C4150D0-7B13-446D-9D8E-866B66723320}']
procedure BeginRead;
procedure EndRead;
procedure BeginWrite;
procedure EndWrite;
end;
然后我们在运行时决定使用哪种实现。如果我们使用的是Windows Vista或新版本,请使用Window自己的SlimReaderWriter
,否则请回退到Omni
版本:
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;
注意:任何代码都会发布到公共域中。无需归属。
答案 2 :(得分:2)
Delphi TMultiReadExclusiveWriteSynchronizer
非常复杂 - 可以递归获取,您可以从Read
更新为Write
。
这带有成本,在这种情况下意味着管理每个线程的共享状态桶。由于Windows线程局部机制(可通过threadvar
访问)过于简单(无法处理多个MREWS实例),因此以相当低效的方式完成 - 请参阅RTL或JCL源 - 实现相当类似,共享不良性能和更新死锁风险。
首先确保您确实需要MREWS功能 - 我假设,根据工作负载的锁定开销的比例大小,使用TCriticalSection
会更好。
如果您确实需要它,请使用Delphi实现并注意BeginWrite
中可能隐藏的解锁 - 查看它的文档并返回值含义。
可以使用SRW
函数或内联汇编实现类似Vista的Interlocked
,但在大多数情况下,这是不值得的。
答案 3 :(得分:0)
JCL有一个MREWS,它是一个可能适合您的不同实现。不确定它需要什么版本的Windows。
http://wiki.delphi-jedi.org/wiki/JCL_Help:TJclMultiReadExclusiveWrite
http://wiki.delphi-jedi.org/index.php?title=JEDI_Code_Library
答案 4 :(得分:0)
试试这个?它可以用作普通变量:
type myclass=class
Lock:TOBRWLock;
function ReadIt:Integer;
procedure WriteIt(A:Integer);
end;
function ReadIt:Integer;
begin;
Lock.LockRead;
Result:=GetVal;
Lock.UnLockRead;
end;
还有很大的改进空间,你可以从这里建立有利于上述阅读的品种,或者根据需要采取不同的行动。
const ldFree = 0;
ldReading = 1;
ldWriting = 2;
type TOBRWLock = record
[Volatile]WritersWaiting,
ReadersWaiting,
ReadersReading,
Disposition : Integer;
procedure LockRead;
procedure LockWrite;
procedure UnlockRead;
procedure UnlockWrite;
procedure UnReadWrite;
procedure UnWriteRead;
end;
procedure TOBRWLock.LockRead;
var SpinCnt : NativeUInt;
I : Integer;
begin
SpinCnt:=0;
TInterlocked.Increment(ReadersWaiting);
repeat
if (Disposition=ldReading)
then begin
I:=TInterlocked.Increment(ReadersReading);
if (Disposition<>ldReading) or (I=1)(*Only 1 read reference or Disposition changed, suspicious, rather retry*)
then begin
TInterlocked.Decrement(ReadersReading);
continue;
end
else begin(*Success*)
TInterlocked.Decrement(ReadersWaiting);
break;
end;
end;
if (WritersWaiting<>0)or(Disposition<>ldFree)
then begin
SpinBackoff(SpinCnt);
continue;
end;
if TInterlocked.CompareExchange(Disposition,ldReading,ldFree)=ldFree
then begin
TInterlocked.Increment(ReadersReading);
TInterlocked.Decrement(ReadersWaiting);
break;
end;
SpinBackoff(SpinCnt);
until False;
end;
procedure TOBRWLock.LockWrite;
var SpinCnt : NativeUInt;
begin
SpinCnt:=0;
TInterlocked.Increment(WritersWaiting);
repeat
if (Disposition<>ldFree)
then begin
SpinBackoff(SpinCnt);
continue;
end;
if TInterlocked.CompareExchange(Disposition,ldWriting,ldFree)=ldFree
then begin
TInterlocked.Decrement(WritersWaiting);
break;
end
else SpinBackoff(SpinCnt);
until False;
end;
procedure TOBRWLock.UnlockRead;
begin
{$IFDEF DEBUG}
if Disposition<>ldReading
then raise Exception.Create('UnlockRead a lock that is not Reading');
{$ENDIF}
TInterlocked.Decrement(ReadersReading);
if ReadersReading=0
then begin;
if TInterlocked.CompareExchange(Disposition,ldFree,ldReading)<>ldReading
then raise Exception.Create('Impossible 310');
end;
end;
procedure TOBRWLock.UnlockWrite;
begin
{$IFDEF DEBUG}
if Disposition<>ldWriting
then raise Exception.Create('UnlockWrite a lock that is not Writing');
{$ENDIF}
if TInterlocked.CompareExchange(Disposition,ldFree,ldWriting)<>ldWriting
then raise Exception.Create('Impossible 321');
end;
procedure TOBRWLock.UnReadWrite;
var SpinCnt : NativeUInt;
begin
{$IFDEF DEBUG}
if Disposition<>ldReading
then raise Exception.Create('UnReadWrite a lock that is not Reading');
{$ENDIF}
TInterlocked.Increment(WritersWaiting);
SpinCnt:=0;
repeat
if ReadersReading=1(*Only me reading*)
then begin;
if TInterlocked.CompareExchange(Disposition,ldWriting,ldReading)<>ldReading(*Must always succeed*)
then raise Exception.Create('Impossible 337');
TInterlocked.Decrement(ReadersReading);
TInterlocked.Decrement(WritersWaiting);
break;
end;
SpinBackoff(SpinCnt);
until False;
end;