PB没有提供完整的多线程机制,只是提供了一种通过Sharedobject进行线程间通信的方式, (猜测)是以一个新线程启动另一个虚拟机, 并在其中实例化一个对象来运行一段程序(函数), 并在完成后执行回调。
这个具体概念目前还不是特别清晰, 从SharedObjectRegister函数的帮助中看, 原文写的是"opens a separate runtime session", 直译是打开一个单独的运行时会话。结合之前网上看到的分析, 在执行SharedObjectRegister函数时,就会启动一个新的虚拟机(pbvm)。所以所有的全局变量都是在各自的虚拟机中存在, 各有各的内存空间, 所以肯定无法用全局变量什么的进行线程间通信。
sharedobject这个命名很有意思, 从pb帮助上看, pb把在"新虚拟机"中创建的这个对象实例称为"共享对象", 应该是说这个新对象虽然不在当前主线程中, 但是被“共享”给当前主线程访问。这个应该是PB提供的一种专用的线程间通信机制吧。
具体来看, SharedObjectRegister函数, 传递了两个string参数, 第一个是类名, 第二个是实例名。这样,在新虚拟机中,就创建了一个该类的实例对象变量(在新虚拟机中实例化了一个对象)。那么怎样从当前线程中访问这个新线程中的“共享”对象呢,通过SharedObjectGet函数。
SharedObjectGet函数的目的, 就是把新线程中共享对象的引用赋值给当前主线程中的该类的一个实例变量, 从pb语法上理解, 也就是完成了一个指针变量间的赋值。只是在pb的帮助中, 使用的是引用(reference)这个概念,没有使用指针这个名词。 为了方便区分, 我们可以把主线程中这个对象引用变量, 称为"代理对象", 与"共享对象"相对应。
从表面现象来猜测,pb对指向共享对象的指针(指向的是另一个虚拟机中的内存空间)进行了特殊的处理,可以在主线程直接访问,可能是某一种特殊的"代理机制"
如果不需要回调, 那么通过使用代理对象, 去调用位于另一个虚拟机中的"共享对象"的函数就可以了。
如果需要回调, 就要在定义共享变量的类时, 设置一个instance级的不可视对象, 这个(新虚拟机中的)实例变量实际上会用来存放主线程中的一个对象的引用, 也可以理解为新虚拟机中的"代理对象", 用来从新虚拟机中调用主线程中的对象函数
关于回调, 在pb的SharedObjectGet函数帮助中有例子, 实际是把主线程中的不可视对象的指针传递给了共享对象。例子中使用的是窗口级实例变量, 这个似乎有些问题, 因为在窗口关闭后, 这个回调用的对象指针就不知指向的是什么了。或者, pb通过引用计数什么的做了单独的处理?
(23.1.19补充) 后来在"路人甲"大神的一篇文章中看到了更详细的分析:
1.所有在新线程中用到的全局变量, 都需要在新线程中进行初始化和赋值
2.主线程中创建的callback对象(nvo对象), 首先可以看作是一个传递数据的"容器", 但这些数据在新线程中访问是有限制的, 只有基本数据类型可以直接在新线程中访问(例如:数值型/字符串/字符/日期/时间/布尔/byte/blob这些, 同时也包括它们的数组, 还有只包含基本数据类型的结构变量)
3.callback对象中的方法, 在设计时, 往往可以分为两类,一类是用在主线程中, 多数用于向"容器"中填写数据, 另一类是在新线程中调用, 一般用于向主线程发送通知信息(通过post方式调用)
用于在主线程中调用的这些方法, 可以操作的数据类型, 当然是不受限制的, 可以直接访问"容器"中的"引用类型"的变量(对象);
但是在新线程中调用的方法, 都不可以访问直接访问"容器"中的引用类型的对象
我自己曾经因为用"="赋值, 把"容器"中的结构变量成员, 向新线程中的本地结构变量直接赋值, 导致新线程异常中止, 具体原因是这个结构变量中, 包含有引用类型的对象
4.新线程中, 可以正常open窗口, 弹出messagebox窗口(阻塞)这些可视对象的,
就像路神文章中说的, 只要你像使用单线程那样去使用新线程中的所有对象,
只要是在新线程中实例化的对象, 都是可以正常使用的
说到底, 还是主线程与新线程都是使用各自的地址空间, 所谓"井水不犯河水", 是不允许跨界访问的.
在主线程中创建的"容器"变量, 它的成员数据和方法, 都是在主线程的地址空间中, 只是把它的引用, 传递给了新线程, 所以才有上面的1.2.3条的限制, 新线程只能访问容器中的"基本数据类型"变量