进程、线程和单元

进程是虚拟内存空间、代码、数据和系统资源的集合。 线程是在进程中串行执行的代码。 处理器执行线程,而不是进程,因此每个应用程序至少有一个进程,并且一个进程始终至少有一个执行线程,称为主线程。 除了主线程之外,进程还可以有多个线程。

进程通过消息相互通信,使用 Microsoft 的远程过程调用 (RPC) 技术相互传递信息。 对于调用方而言,远程计算机上的进程调用与同一计算机上的另一个进程调用之间没有区别。

线程开始执行时会一直持续到终止,或者直到被优先级较高的线程中断(由用户操作或内核的线程计划程序中断)。 每个线程可以运行单独的代码片段,或者多个线程可以执行相同的代码片段。 执行同一代码块的线程维护单独的堆栈。 进程中的每个线程共享该进程的全局变量和资源。

线程计划程序根据进程的优先级类属性和线程的基本优先级的组合来确定执行线程的时间和频率。 通过调用 SetPriorityClass 函数来设置进程的优先级类属性,并通过调用 SetThreadPriority 来设置线程的基本优先级。

多线程应用程序必须避免两个线程问题:死锁争用。 当每个线程等待另一个线程执行某些操作时,将发生死锁。 COM 调用控件有助于防止对象之间的调用发生死锁。 当一个线程先于所依赖的另一个线程完成,导致前者因后者尚未提供有效的值而使用未初始化的值时,会出现争用情况。 COM 提供一些专为帮助避免进程外服务器中的争用情况而设计的函数。 (请参阅进程外服务器实现帮助程序。)

单元和 COM 线程体系结构

虽然 COM 支持在引入多个执行线程之前普遍存在的每个进程单个线程模型,但可以编写代码来利用多个线程,从而生成更有效的应用程序,方法是允许执行一个线程,而另一个线程等待一些耗时的操作完成。

注意

使用多个线程不保证性能更佳。 事实上,由于线程分解是一个难题,因此使用多个线程通常会导致性能问题。 关键是仅当非常确定正在执行的操作时才使用多个线程。

 

一般来说,查看 COM 线程体系结构最简单的方法是将进程中的所有 COM 对象视为划分为称为单元的组。 COM 对象正好位于一个单元中,从某种意义上来说,其方法只能由属于该单元的线程直接调用。 任何其他线程都必须通过代理调用该对象。

有两种类型的单元:单线程单元多线程单元

  • 单线程单元只包含一个线程,因此在单线程单元中的所有 COM 对象只能从属于该单元的一个线程接收方法调用。 对单线程单元中 COM 对象的所有方法调用都与单线程单元线程的 Windows 消息队列同步。 具有单个执行线程的进程只是此模型的一种特殊情况。
  • 多线程单元由一个或多个线程组成,因此多线程单元中的所有 COM 对象都可以直接从属于多线程单元的任何线程接收方法调用。 多线程单元中的线程使用名为 free-threading 的模型。 对多线程单元中的 COM 对象的调用由对象本身同步。

注意

有关同一进程中单线程单元与多线程单元之间通信的说明,请参阅单线程和多线程通信

 

一个进程可以有零个或多个单线程单元和零个或一个多线程单元。

在一个进程中,主单元是第一个初始化单元。 在单线程进程中,这是唯一一个单元。 调用参数在单元之间封送,COM 通过消息传送处理同步。 如果将进程中的多个线程指定为自由线程,则所有自由线程都驻留在单个单元中,参数将直接传递到单元中的任何线程,并且必须处理所有同步。 在包含自由线程和单元线程的进程中,所有自由线程都驻留在单个单元中,所有其他单元都是单线程单元。 执行 COM 工作的进程是单元的集合,其中最多包含一个多线程单元,但包含任意数量的单线程单元。

COM 中的线程模型为使用不同线程体系结构的客户端和服务器提供协同工作机制。 自然支持不同进程中具有不同线程模型的对象之间的调用。 从调用对象的角度来看,无论调用的对象采用哪种线程处理方式,对进程外部的对象的所有调用行为都相同。 同样,从被调用对象的角度来看,无论调用者的线程模型如何,都会触发相同的调用行为。

即使使用不同的线程模型,客户端和进程外对象之间的交互也非常简单,因为客户端和对象位于不同的进程中。 在客户端和服务器之间交错的 COM 可以使用标准封送和 RPC 为线程模型提供代码进行互操作。 例如,如果多个自由线程客户端同时调用单线程对象,COM 将通过在服务器的消息队列中放置相应的窗口消息来同步调用。 每次检索和调度消息时,对象的单元都会收到一个调用。 但是,必须谨慎确保进程内服务器与其客户端正确交互。 (参阅进程内服务器线程问题。)

使用多线程模型编程时最重要的问题是使代码保持线程安全,以便专为特定线程提供的消息仅转到该线程,并且线程访问受到保护。

有关详情,请参阅以下主题: