编程语言
首页 > 编程语言> > DRT中面向异步程序结构的自动引用工具库

DRT中面向异步程序结构的自动引用工具库

作者:互联网

面向异步程序结构的自动引用工具库

为了配合高性能服务端要求的的异步代码结构,DRT中附带了一套自动引用工具,帮助实现以引用计数的方式进行内存生命周期管理,该工具包括有自动计数对象引用、自动计数指针引用以及任务同步器三套工具,这些工具所在的单元为DRT.YWTypes。DRT项目组中的TestAutoRef.exe项目,就是这些工具的Demo代码。

自动计数对象引用

  1. var A,B : R<TClassA>;  
  2.     C : WR<TClassA>;  

此处为变量定义形式,A,B为关于TClassA类型的强引用变量,C为弱引用变量

  1.     D : TClassA;  
    1. begin  
    2.   Put('=======Object Auto Ref Demo Started==========');  
    3.   ShowValue([A,B,C]);

此处打印列出ABC三个变量的初始值,可以看到虽然是函数局部变量,这些引用类型是具有自动初始化的功能的。  

  1.   PUT;  
  2.   Put('A := TClassA.Create;');  
  3.   A := TClassA.Create;  
  4.   ShowValue([A,B,C]);

这里演示对引用变量赋值的方式,就如同普通类变量一样使用即可  

  1.   PUT;  
  2.   B :=TClassA.Create;  
  3.   A :=B;  
  4.   ShowValue([A,B]);  

这里可以在MEMO的打印记录中看到,对A赋新值,导致A的旧引用实例会被释放

  1.   PUT;  
  2.   Put('D:=A');  
  3.   D := A;  
  4.   ShowValue([A,B,C,D]);  

这里演示从引用变量取得常规类实例的方法,引用类型与对象类型,可以在参数中直接取代常规对象使用。

  1.   PUT;  
  2.   PUT('C := B;');  
  3.   C  := B;  
  4.   ShowValue([A,B,C]); 

这里演示弱引用类型的赋值方法 

  1.   PUT;  
  2.   PUT('C.O.SayHello;');  
  3.   C.O.SayHello;  

这里演示通过引用变量调用被引用对象的成员函数

  1.   PUT;  
  2.   begin  
  3.     var E : R<TClassA>;  
  4.     PUT('E := C;');  
  5.     E := C;  
  6.     ShowValue([A,B,C,D,E]);  

这里演示从弱引用变量赋值到强引用变量,变量E的作用域是被限制在begin end之间

  1.     PUT;  
  2.     PUT('A:=nil; B:=nil');  
  3.     A:=nil; B := nil;  
  4.     ShowValue([A,B,C,D,E]);  

A和B的引用都被解除,变量E是实例最后的引用,在其作用域退出后,将会自动解除引用

  1.     PUT;  
  2.     PUT('E end of life');  
  3.   end;  
  4.   ShowValue([A,B,C,D]);  

这里可以看到类实例的释放记录打印,同时可以看到随着实例释放,变量C自动被设定为nil,而常规类变量D无法感知实例的释放情况

  1.   PUT;  
  2.   PUT('D.Free.......');  
  3.   try  
  4.     D.Free;  
  5.   except on E: Exception do  
  6.     PUT(E.Message);  
  7.   end;  
  8.   Put('========Object Auto Ref Demo Ended===========');  

调用D.free,抛出异常证实常规类变量D的地址值是无效的。

 

  1. procedure MainForm.OnEvent(Sender : TObject);  
  2. var R : R<TMyClass>;  
  3. begin  
  4.   R := WR // a variable of WR<TMyClass>  
  5.   if R<>nil then begin  
  6.       R.O.DoSomeThing;  
  7.   end;  
  8. end;  

 

自动计数指针引用

  1. var A,B : REFPTR;  
  2.     C : WREFPTR;  
  3.     D : Pointer;  

变量定义形式,REFPTR为强引用指针,WREFPTR为弱引用指针

  1. begin  
  2.   Put('=======Pointer Auto Ref Demo Started==========');  
  3.   ShowMem;  
  4.   A.Alloc(MEMSIZE);  
  5.   ShowMem;  

这里通过两次对比内存占用情况,演示REFPTR类型的Alloc方法

  1.   GetMem(D,MEMSIZE); B := D;  
  2.   C := B;  
  3.   ShowValue([A,B,C]);  

这里演示普通指针对REFPTR的赋值,以及REFPTR对WREFPTR的赋值,它们在作为参数传递给ShowValue函数时,都能够自动兼容Pointer类型

  1.   ShowMem;  
  2.   B := A;  
  3.   ShowMem;  

第一次显示的内存占用为A,B各指向不同内存块时的占用,第二次时,B指向新内存块,旧引用计数降到零触发内存释放,从内存占用数值可以看出内存B的旧内存已经被释放

  1.   ShowValue([A,B,C]);  

B和C原本指向同一片内存,C是弱引用所以不能阻止B释放内存,但是C可以感知到B的内存释放行为,其值已经自动变为nil。

  1.   begin  
  2.     var E: REFPTR  := A;  

变量E为作用域局限在begin end之间的强引用变量

  1.     A := nil; B := nil;  
  2.     ShowMem;  

A,B变量解除引用,但由于E保持了引用,内存占用情况并没有变化

  1.     ShowValue([A,B,C,D,E]);  
  2.   end;  
  3.   ShowMem;  

变量E的作用域结束后,从内存占用情况可以看到E引用的内存已经被释放

  1.     ShowValue([A,B,C,D]);  

传统的指针变量D依然指向一个地址,但是该地址已经被释放,D现在是一个野指针

  1.   Put('========Pointer Auto Ref Demo Ended===========');  
  2.   Put;  
  3. end;  

 

任务同步器

DRT提供的任务同步工具类型为TaskSyncer,它是一个带方法的托管记录体,所以使用时无须创建,它自带引用计数,会自动释放资源,提供了4个方法帮助进行线程同步:它的功能很简单,就是对待完成任务数量进行计数,当计数到0时在当前线程立即执行善后代码,从而完全避免了Waitfor机制。

 

Demo代码如下:

  1. var T : TaskSyncer;  
  2. begin  
  3.   Put('=======Task Syncer Demo Started==========');  
  4.   T.OnAllDone(procedure begin  
  5.     UIPut('All Task Done!');  
  6.     UIPut('========Task Syncer Ref Demo Ended===========');  
  7.     UIPut;  
  8.   end); 

设定任务完成回调匿名函数 

  1.   T.AddTask(20);  

预设待完成任务为20个

  1.   for var i := 1 to 20 do begin  
  2.     var F := procedure(i : integer) begin 

在匿名函数一文中提到过的将循环变量传递进内层匿名函数的小手段 

  1.       TTask.Run(procedure begin  
  2.         UIPut('Task '+i.ToString+' Start.....');  
  3.         var a := random(3000)+200;

用随机数生成sleep的时间模拟不确定的任务执行时间  

  1.         if a and 3 = 1 then begin  
  2.           T.AddTask;

模拟在意外情况下临时新增任务的场景  

  1.           TTask.Run(procedure begin  
  2.             UIPut('Additional Task From Task '+i.tostring+' Running...');  
  3.             Sleep(2000);  
  4.             UIPut('Additional Task From Task '+i.tostring+' Done!');  
  5.             T.DoneTask; 

新增任务也是并发执行,新增任务也要显式用DoneTask声明自己的任务已完成 

  1.           end);  
  2.         end;  
  3.         sleep(a);  
  4.         UIPut('Task '+i.ToString+' Done!');  
  5.         T.DoneTask;  

用Sleep模拟任务执行过程,用DoneTask声明任务完成

  1.       end);  
  2.     end;  
  3.     F(i);  
  4.   end;  
  5. end; 

函数直到结束也没有Waitfor任何信号,善后工作由最后一个完成任务的线程执行。 

标签:异步,begin,end,变量,程序结构,DRT,内存,PUT,引用
来源: https://www.cnblogs.com/ywthegod/p/16287033.html