|
Execute方法
…
通过Synchronize发送消息给主线程并等待
…
SyncProc方法
一段受Synchronize保护的代码
WM_NULL消息处理
创建线程
再来看类的实例化细节。线程类的实例化与一般类大体相同,但也有其不同之处,最大的不同就是,它在进行实例化的同时还创建了一个线程(即ThreadProc的执行)。
对于一般类的实例化来说:首先是调用类构造器(Constructor)Create,而Create的工作首先就是在进程的堆空间中分配类的数据区(包括类的数据成员和一些必要额外数据),然后把这个数据区的指针作为Self参数调用Create完成构造工作。此后,我们所用到的类实例,其实具体的就是这个数据区。比如我们要调用某个类实例(SomeObject)的某成员方法(SomeMemberMethod)如下:
SomeObject.SomeMemberMethod(Parameters);
其实就等效于:
TSomeClass.SomeMemberMethod(SomeObject, Parameters);
其中TSomeClass就是类实例SomeObject对应的类,而相应的,类实例SomeObject被作为隐含参数Self传递到方法内部。
而在方法内部访问类数据成员(SomeMemberData)时:
Function TSomeClass.SomeMemberMethod(Parameters) : SomeType; // 因为实际上Self是隐含传递的
Begin
Result := SomeMemberData;
End;
其实等效于Self.SomeMemberData。
对于线程类的情况,与上面说的基本相同。
但是有一个最大的不同就在于:线程类实例会创建自己独立的栈(由线程函数ThreadProc隐含创建),而普通类是使用主线程栈的。这就是意味着,在子线程中使用局部变量是安全的,因为局部变量是分配在栈中的。各个线程都有自己的栈(包括主线程),而且一般情况下是无法直接访问别的线程的栈空间的,除非是一些极端的情况(如将局部变量通过指针传给其它线程供操作),局部变量都不需要访问冲突保护。
但这不表示线程类数据成员(如前面的FData)安全,因为它们是分配在进程的堆空间中。当然,每个线程类对象都有各自独立的数据成员,正常情况下只要不互相访问,它们仍然是安全的。但是如果需要让别的线程,特别是主线程使用线程类数据成员,就一定要考虑到访问冲突保护的问题(如前面的例子,FData通过Foo函数导致了主线程对它的访问),因为这种冲突访问通常是通过线程类本身的方法/属性间接进行的,有时很容易被忽视。
实现后的线程类运行时情况如下图:
主窗体代码
线程类代码
SomeMemberMethod
主线程栈
堆空间
线程类实例1数据:SomeMemberData
线程类实例2数据:SomeMemberData
线程1的栈
线程2的栈 代码区 数据区
上一页 [1] [2] [3] 下一页 |