Single-Threaded公寓
使用单线程单元 (单元模型过程) 提供了一个基于消息的范例,用于处理并发运行的多个对象。 它允许线程编写更高效的代码,同时等待一些耗时的操作完成,以允许执行另一个线程。
初始化为单元模型进程且检索和调度窗口消息的进程中的每个线程都是单线程单元线程。 每个线程位于自己的公寓内。 在单元中,无需封送即可传递接口指针,因此,一个单线程单元线程中的所有对象都直接通信。
所有在同一线程上执行的相关对象的逻辑分组,因此必须具有同步执行,可以位于同一单线程单元线程上。 但是,单元模型对象不能驻留在多个线程上。 必须在拥有线程的上下文中调用其他线程中的对象,因此在调用代理时,分布式 COM 会自动切换线程。
进程间模型和线程间模型相似。 如果需要将接口指针传递到另一个单元中的某个对象, (位于同一进程中的另一个线程上) ,则使用不同进程中的对象用于跨进程边界传递指针的同一封送模型。 通过获取指向标准封送对象的指针,可以在单元之间封送接口指针, (单元之间) 处理过程的方式 (。 在 apartments.) 之间传递时,必须封送 (接口指针
单线程单元的规则很简单,但请务必仔细遵循这些规则:
- 每个对象应只位于单线程单元) 内一个线程 (。
- 初始化每个线程的 COM 库。
- 在单元之间传递对象时,封送所有指向对象的指针。
- 每个单线程单元都必须有一个消息循环来处理同一进程中其他进程和单元的调用。 没有对象的单线程单元 (客户端仅) 也需要消息循环来调度某些应用程序使用的广播消息。
- 基于 DLL 或进程内的对象不调用 COM 初始化函数;而是使用注册表中 InprocServer32 键下的 ThreadingModel 命名值注册线程模型。 单元感知对象还必须仔细编写 DLL 入口点。 有一些特殊注意事项适用于进程内线程服务器。 有关详细信息,请参阅 进程内服务器线程问题。
虽然多个对象可以位于单个线程上,但不能在多个线程上生存单元模型对象。
客户端进程或进程外服务器的每个线程都必须调用 CoInitialize,或调用 CoInitializeEx ,并为 dwCoInit 参数指定COINIT_APARTMENTTHREADED。 主单元是首先调用 CoInitializeEx 的线程。 有关进程内服务器的信息,请参阅 进程内服务器线程问题。
对对象的所有调用必须在其单元) 内 (线程上进行。 禁止直接从另一个线程调用对象;以这种自由线程方式使用对象可能会导致应用程序出现问题。 此规则的含义是,在单元之间传递时,必须封送指向对象的所有指针。 COM 为此提供了以下两个函数:
- CoMarshalInterThreadInterfaceInStream 将接口封送到返回到调用方的流对象中。
- CoGetInterfaceAndReleaseStream 取消对流对象中的接口指针取消查询并释放它。
这些函数包装对 CoMarshalInterface 和 CoUnmarshalInterface 函数的调用,该函数需要使用 MSHCTX_INPROC 标志。
一般情况下,封送由 COM 自动完成。 例如,在方法调用中将接口指针作为参数传递给另一单元中的对象或调用 CoCreateInstance 时,COM 会自动执行封送处理。 但是,在某些特殊情况下,应用程序编写器在单元之间传递接口指针而不使用普通 COM 机制,编写器必须手动处理封送处理。
如果一套公寓 (公寓 1) 过程中有接口指针,而另一套公寓 (公寓 2) 需要使用,则公寓 1 必须调用 CoMarshalInterThreadInterfaceInStream 来封送接口。 此函数创建的流是线程安全的,必须存储在单元 2 可访问的变量中。 单元 2 必须将此流传递给 CoGetInterfaceAndReleaseStream 以取消对接口的破坏,并返回指向代理的指针,通过该代理可以访问接口。 主单元必须保持活动状态,直到客户端完成所有 COM 工作 (,因为某些进程内对象在主单元中加载,如 进程内服务器线程问题) 中所述。 以这种方式在线程之间传递一个对象后,很容易将接口指针作为参数传递。 这样,分布式 COM 会为应用程序执行封送处理和线程切换。
若要处理同一进程中其他进程和单元的调用,每个单线程单元必须有一个消息循环。 这意味着线程的工作函数必须具有 GetMessage/DispatchMessage 循环。 如果使用其他同步基元在线程之间通信,则 MsgWaitForMultipleObjects 函数可用于等待消息和线程同步事件。 此函数的文档有这种组合循环的示例。
COM 使用每个单线程单元中的Windows类“OleMainThreadWndClass”创建隐藏窗口。 对对象的调用作为此隐藏窗口的窗口消息接收。 当对象的单元检索并调度消息时,隐藏窗口将收到它。 然后,窗口过程将调用对象的相应接口方法。
当多个客户端调用对象时,这些调用将排队在消息队列中,对象将在每次其单元检索和调度消息时接收一个调用。 由于调用由 COM 同步,并且调用始终由属于该对象的单元的线程传递,因此该对象的接口实现不需要提供同步。 单线程单元可以实现 IMessageFilter ,以允许它们在必要时取消呼叫或接收窗口消息。
如果某个接口方法实现检索和调度消息或对另一个线程发出 ORPC 调用,从而导致同一单元) 将另一个调用传递到对象 (,则可以重新输入该对象。 OLE 不会阻止在同一线程上重新进入,但它可以帮助提供线程安全性。 这与在处理消息时检索和调度消息时窗口过程重新输入的方式相同。 但是,调用另一个单线程单元服务器的进程外单线程单元服务器将允许重新进入第一台服务器。
相关主题