Delphi 在多线程中会应用到的读写锁-“多读一写”同步器-TMultiReadExclusiveWriteSynchronizer

使用Delphi很多年了,但对VCL的精华还是了解甚少,今天在看国外一个使用Delphi开发的软件加密打包工具enigmavb(Enigma Virtual Box)时,发现国外的技术牛人确实有很多过人之处,该工具直接Hook NTDLL的函数,实现将需要保护的软件和其运行所需文件(DLL/OCX/TXT/INI/DOC)和注册表全部打包到单一的EXE程序,运行时通过HOOK ZwXXXFile、ZwXXXKey和其他Process、Section等相关函数,在EXE进程内部虚拟出一个运行环境,实现类似SandBox的功能。

在研究期间发现其中使用了一个多线程同步访问对象的类:TMultiReadExclusiveWriteSynchronizer

功能:

当进程中有共享对象需要被多个线程读写访问时,通常做法是使用CriticalSection进行处理,但在某些需要细致优化的程序开发中,会再次细分这些需要被读写的共享对象,可分为下面2类:

1)经常被读,偶尔被写

2)经常被读,也经常被写

对于共享对象,当其数据在内存中没发生变化时,不管多少个线程去读,都不会造成错误,当但有线程在读时,这时候又有其他线程在写,那将会出现读出来的对象可能是有部分被写了,最终读出来的数据相当于不完整或损坏的,因此会发生错误。 所以当共享对象被写时,才需要对其进行保护,当没有线程在写时,是不需要保护的。

基于上面的特点:

第一类共享对象,使用CriticalSection明显会降低读的效率,这种情况下,使用TMultiReadExclusiveWriteSynchronizer进行保护则可提高访问效率。

第二类共享对象,当然也只有选择CriticalSection或者SpinLock了,当然也可以根据实际情况自主实现一些高效的的保护锁。

TMultiReadExclusiveWriteSynchronizer在VCL中经过复杂的封装后,只需要关心下面这几个方法即可

procedure BeginRead;

procedure EndRead;

function BeginWrite: Boolean;

procedure EndWrite;

Windows 在Vista后提供了 SRWLock, 据说效率比TMultiReadExclusiveWriteSynchronizer高10倍以上,linux下的rwlock实现有官方的和民间的,多种多样,效率不等。

但是就应用场景而言,TMultiReadExclusiveWriteSynchronizer所牺牲的效率是为了更方便快捷的开发,可以完全替换CriticalSection,没有递归调用的限制。

而SRWLock和rwlock由于自身无法递归调用的缺点,导致开发中需要非常严谨的编写逻辑,相对来说不是那么容易使用,甚至非常容易出现死锁,超高CPU占用等BUG。