多线程单元

在多线程单元模型中,进程中所有已初始化为自由线程的线程驻留在单个单元中。 因此,无需在线程之间封送。 线程不需要检索和调度消息,因为 COM 不会在此模型中使用窗口消息。

可以在单元中的任何线程上运行对多线程单元中对象方法的调用。 调用没有序列化;许多调用可能会同时作用于同一方法或同一对象。 多线程单元中创建的对象必须能够随时处理其他线程中的方法调用。

对象调用不以任何方式序列化,因此多线程对象并发提供最佳性能,并充分利用多处理器硬件进行跨线程、跨进程和跨计算机调用。 但是,这意味着对象的代码必须在其接口实现中提供同步,通常通过使用同步基元(如事件对象、临界区、互斥体或信号灯),本部分稍后将进行相关介绍。 此外,由于对象不控制正在对其进行访问的线程的生存期,因此特定于线程的状态不会存储在对象中(线程本地存储中)。

以下是有关多线程单元同步的一些重要注意事项:

  • COM 仅为单线程单元提供调用同步。
  • 多线程单元不在进行调用时接收调用(在同一线程上)。
  • 多线程单元无法进行输入同步调用。
  • 多线程单元中的异步调用将转换为同步调用。
  • 对于多线程单元中的任何线程,不调用消息筛选器。

若要将线程初始化为自由线程,请调用 CoInitializeEx,并指定 COINIT_MULTITHREADED。 有关进程内服务器线程处理的信息,请参阅进程内服务器线程处理问题

多个客户端可以同时从不同的线程调用支持自由线程处理的对象。 在自由线程进程外服务器中,COM 通过 RPC 子系统在服务器进程中创建线程池,其中任何线程可随时传送一个或多个客户端调用。 进程外服务器还必须在其类工厂中实现同步。 自由线程进程内对象可以从客户端的多个线程接收直接调用。

客户端可以在多个线程中执行 COM 工作。 所有线程都属于同一个多线程单元。 接口指针直接在多线程单元中的线程之间传递,因此接口指针不会在其线程之间封送。 多线程单元中不使用消息筛选器(IMessageFilter 实现)。 客户端线程将在对单元外对象进行 COM 调用时挂起,并在调用返回时恢复。 进程之间的调用仍由 RPC 处理。

使用自由线程模型初始化的线程必须自行实现同步。 如本部分前面所述,Windows 通过以下同步基元启用此实现:

  • 事件对象提供一种方法向一个或多个已发生事件的线程发出信号。 进程中的任何线程都可以创建事件对象。 事件创建函数 CreateEvent 返回事件的句柄。 创建事件对象后,具有对象句柄的线程可以等待一段时间,然后再继续执行。
  • 临界区用于存放部分代码,这些代码需要对一组共享数据进行独占访问,然后才能对该数据执行操作,并且只供单个进程中的线程使用。 临界区类似于旋转栅,一次只能传递一个线程,如下所示:
    • 为了确保一次没有多个线程访问共享数据,进程的主线程会分配全局一个 CRITICAL_SECTION 数据结构并初始化其成员。 进入临界区的线程将调用 EnterCriticalSection 函数并修改数据结构的成员。
    • 尝试进入临界区的线程将调用 EnterCriticalSection,以检查 CRITICAL_SECTION 数据结构是否已修改。 如果是,则另一个线程当前位于临界区,后续线程处于休眠状态。 离开临界区的线程将调用 LeaveCriticalSection,以重置数据结构。 当线程离开临界区时,系统会唤醒其中一个休眠线程,该线程随后便会进入临界区。
  • 除了不同进程中运行的线程可以访问互斥体之外,互斥体执行的功能与临界区相同。 拥有互斥体对象就像争论总有尽头一样。 一个进程通过调用 CreateMutex 函数创建互斥体对象,随后返回一个句柄。 请求互斥体对象的第一个线程将获取其所有权。 当线程完成互斥体请求后,所有权会按先到先得的方式传递到其他线程。
  • 信号灯用于维护某些可用资源的引用计数。 线程通过调用 CreateSemaphore 函数并将指针传递给资源、初始资源计数和最大资源计数,为资源创建信号灯。 此函数返回一个句柄。 请求资源的线程在 WaitForSingleObject 函数调用中传递其信号灯句柄。 信号灯对象轮询资源以确定其是否可用。 如果是,信号灯会递减资源计数,并唤醒等待线程。 如果计数为零,则线程将保持休眠状态,直到另一个线程释放资源,使信号灯按 1 递增计数。

跨单元访问接口

选择线程模型

进程内服务器线程问题

进程、线程和单元

单线程通信和多线程通信

单线程单元