[教程]ZAsync 异步编程之四:关于匿名函数和线程变量

[匿名函数]

在 Delphi 中,匿名函数实际上是一个接口,这一点我们在前面的文章中已经做出了明确的提示。从前面的分析我们可以看出:

1、在匿名函数中使用的局部变量,实际内存是对应的结构成员的位置,而不是当前函数栈上的位置。

2、如果一个接口局部变量被匿名函数引用,那么应该在用完后,设置为空,以便减小引用计数,避免内存泄露。

所以,一个良好的编码习惯是始终在不使用接口时,将其赋值为空,而不是等待其被自动清理。

[线程变量]

线程变量如果是简单类型,那么它会随着线程的释放,而被操作系统释放。但线程变量是指针类型的变量,如接口/对象/记录等复合类型,实际上其是在堆上分配的内存,并不会随着线程的释放而释放,需要我们人工进行处理。TZAsync 对这些类型的对象进行了一些处理,以减少您自己维护的代码量:

  • TZAsync.RegisterThreadLeak 注册一个线程中分配的对象或接口,而这个接口或对象在线程异常中止时,无法被及时释放。
  • TZAsync.UnregisterThreadLeak 用来移除经由前述接口注册的的对象或接口,改由用户自行释放

举一个简单的例子:

threadvar MyList:TList;
....
MyList:=TList.Create;
TZAsync.RegisterThreadLeak(MyList);
...

如果上述代码在线程中执行过程中,线程结束时,如果用户没有手动释放 MyList 实例,那么 TZAsync 会在线程结束后,自动检查并释放 MyList 的值,从而减少内存泄露的问题。

如果我们的代码正常运行,在正常线程结束前,我们可以调用下面的代码来取消委托管理:

TZAsync.UnregisterThreadLeak(MyList);

接口同理即可,但需要注意:

  • 如果接口有被非线程变量引用,那么用户不应该委托给 TZAsync 管理变量的生命周期。
  • TZAsync 对接口的释放是采用暴力减少引用计数到0来释放,但如果计数在调用 _Release 多次后没有变化,则不会处理(比如 TComponentInterfaceDelegate 实现始终返回-1)
分享到: