用TMoitor的好处显而易见,比起SyncObjs单元定义的类(比如TCriticalSection),不用和os内核打交道就把事情办了,这也是向java看齐,和java的关键字synchronized 一样。为了实现TMonitor,Delphi的TObject.InstanceSize从4变成了8(除了VMT的4byte,附加了4byte),这样就让任何类都可以给TMonitor来使用。可以看这里。
使用方法异常简单
var lock: TObject; lock:= TObject.Create; //定义一加锁变量 begin try System.TMonitor.Enter(lock); ... finally System.TMonitor.Exit(lock); end; end;TMonitor的Wait和Pulse以及PulseAll,java的wait, notify, notifyall相当。
提到加锁,就能想到单例模式时常用的一段代码,比如
TSingleton = class strict private class var FInstance: TSingleton; public class function Instance: TSingleton; end; { TSingleton } class function TSingleton.Instance: TSingleton; begin if not Assigned(FInstance) then FInstance := TSingleton.Create; Result := FInstance; end;这段代码其实是有问题的,因为:
判断完是否为nil后,执行下一句之前,可能FInstance有改变。也就是说FInstance可能被赋值了,然后如果再去赋值一次,从而导致泄漏一个TSingleton的实例
要解决这个,1个是用上面的TMonitor给加锁,但是这样岂不是每次调用TSingleton.Instance时都得加锁解锁一回,与理不容。
另一个法子则是用SyncObjs新增的类(XE后加的),TInterlocked。
没这个类之前,要解决这个,又得和os内核打交道,用windows api的InterlockedExchange来完成(Delphi XE前的System.pas里的InterlockedCompareExchangePointer函数没公开出来的)。但是现在有了TInterlocked,就又可以不和os打交道就完成了
代码如下
class function TSingleton.Instance: TSingleton; begin if FInstance = nil then begin Result := TSingleton.Create; if TInterlocked.CompareExchange(Pointer(FInstance), Pointer(Result), nil) <> nil then Result.Free;//CompareExchange会返回FInstance之前值,如果不是nil,表示已经被赋值过了,那么就free掉现在新建的实例。 end; Result := FInstance; end;查看TInterlocked的代码,可以看到,EMBT没有使用windows api去实现的,而是用汇编,汇编俺早忘光了。
代码如下:
class function TInterlocked.CompareExchange(var Target: Pointer; Value: Pointer; Comparand: Pointer): Pointer; {$IFDEF X64ASM} asm .NOFRAME MOV RAX,R8 LOCK CMPXCHG [RCX],RDX end; {$ELSE !X64ASM} {$IFDEF X86ASM} asm XCHG EAX,EDX XCHG EAX,ECX LOCK CMPXCHG [EDX],ECX end; {$ENDIF X86ASM} {$ENDIF !X64ASM}和TMonitor类一样,也是靠汇编的LOCK前缀搞定的。
Intel这么解释LOCK
Causes the processor's LOCK# signal to be asserted during execution of the accompanying instruction (turns the instruction into an atomic instruction). In a multiprocessor environment, the LOCK# signal insures that the processor has exclusive use of any shared memory while the signal is asserted.
所以,用ASM的lock,其实并不是个很好的法子(当然和cpu直接比起和os api打交道,要好非常多的了),因为lock操作冻结了所有cpu的core,这在多core的cpu上几乎无法容忍,Synopse看到这个问题(比如delphi的string,interface的引用计算也是靠LOCK来整的),写了新的SynScaleMM内存管理器,提出了别的实现方法,开源的。但是话又说回来,现在的硬件这么快(今天测试玩HP DL580 G7 Intel Xeon E7-4870,tmd 4个4870cpu,那个快啊),lock就lock呗,FastMM已经工作得非常棒了。
最后,TMonitor的设计和实现是Allen Bauer做的,用得爽没事去溜达他的blog吧。另外要提一下TMonitor是个record,不是class哦,下次再谈谈为啥。
没有评论:
发表评论