本文翻译自https://msdn.microsoft.com/en-us/library/ms693344(v=vs.85).aspx
Processes, Threads, and Apartments
进程、线程、套间(为什么会这样翻译,好坑)
进程是虚拟内存空间、代码、数据和系统资源的集合。线程是在进程中被连续执行的代码。处理器执行的是线程,而不是进程,所以每一个应用程序拥有至少一个进程,一个进程通常拥有至少一个执行线程,被称为主线程。一个进程除主线程之外还可以拥有多个线程。
进程间通过消息进行通信,使用RPC技术传递信息至另一个进程。来自远程机器上的进程的调用(call)与来自同一机器上的另一进程的调用(call)之间的呼叫者(caller)没有区别。
当一个线程开始执行,它将会持续下去直到线程被杀死或者被高优先级的线程中断。每个线程都可以运行单独的代码段,或者多个线程可以执行相同的代码段。执行同一代码块的线程各自维持着独立的堆栈。在进程中的每个线程共享全局变量和资源。
进程调度根据进程优先级和线程基本优先级结合决定何时和多久执行线程。调用SetPriorityClass方法设置进程的优先级,调用SetThreadPriority方法设置线程基本优先级。
多线程应用程序必须避免两个线程问题:死锁和竞态(deadlocks and races)。当每一个线程都在等待另一个做某事时发生死锁。COM组件调用控件有助于预防对象之间调用的死锁。当一个线程在另一个其依赖的线程之前完成会发生竞态,导致前者使用未初始化的值,因为后者未提供一个有效值。COM组件提供一些特索的功能设计帮助避免在进程外服务器的竞态条件。
#
死锁都很好理解,
现在说说竞态条件:计算的正确性取决于多个线程的交替执行时序时,就会发生竞态条件。由多线程时序导致的问题。
常见的竞态条件:
1. 先检测、后执行
判断文件是否存在,不存在则创建新文件。
很正常的需求,若多个线程同时执行此代码则会发生竞态条件,导致结果出现各种问题。
2. 延长加载
单例模式下对象的创建:
public class SingletonObj
{
private static SingletonObj instance=null;
public SingletonObj getInstance()
{
if(instance==null)
{
instance = new SingletonObj();
}
return instance;
}
}
好像没什么问题,但是如果有两个线程同时访问getInstance,则可能出现二次创建对象。
竞态条件不一定会产生问题,但是可能导致结果不一致。
“多线程1:竞态条件” 讲得很清晰。
#
The Apartment and the COM Threading Architecture (套间和COM组件线程架构)
虽然COM组件支持在引入多线程执行之前很流行的进程单线程模型(single-thread-per-process model),但是你可以编写代码利用多线程,通过允许单个线程在另一个线程等待时完成执行一些耗时的操作,从而创建更高效的应用程序。
Note 采用多线程并不能保证拥有更好的性能。事实上,因为线程分离是一个难题,使用多线程经常会导致性能问题。关键是除非你非常确信你在做什么的情况下才能使用多线程。
通常,观察COM组件线程架构最简单的方法是将在进程中的所有的COM组件对象划分到被称为apartment(套间)的组。一个COM组件对象只寄存在一个套间中,它的方法只能被属于此套间的线程合法的直接调用,任何其他线程想要调用此对象都必须通过代理。
有两种类型的套间:Single-Threaded Apartments,Multithreaded Apartments.
- 单线程套间只能由一个线程构成,单线程套间的COM对象的所有方法调用都与单线程套间线程的windows消息队列同步。具有单个执行线程的进程只是该模型的特例。
- 多线程套间由一个或多个线程构成,所以所有的COM组件对象都寄宿在一个多线程套间,可以直接的接收来自属于此多线程套间内的任意线程的调用。多线程套间内的线程采用被称为free-threading的模式。在多线程套间内的COM组件对象调用有对象自身进行同步。
Note 关于在同一进程中单线程套间和多线程套间之间的通信的描述,请参考Single-Threaded and Multithreaded Communication
一个进程可以包含一个或多个单线程套间和一个或多个多线程套间。
在一个进程中,第一个被初始化的是主套间。在单线程进程中,只有一个套间。调用参数在套间直接被编排,COM组件通过消息处理同步。如果你在一个进程中指定多线程为线程自由(free-threaded),所有的自由线程寄宿在一个独立的套间内,参数直接传递给任意在套间内的线程,并且你必须处理所有的同步。在一个进程中包含自由线程和套间线程,所有自由线程寄宿在一个单独的套间,其他所有的套间都是单线程套间。一个COM组件工作进程是一个套间集合,最多有一个多线程套间,但是有任意数量的单线程套间。
在COM组件中的线程模型为采用不同线程架构的客户端和服务端工作在一起的机制。天然支持在不同进程中使用不同线程模型的对象之间的调用。从调用者的视角来看,所有对进程外对象的调用都具有相同的行为,不管对象如何被调用。同样的,从被调用者的视角来看,收到的调用行为都是相同的,无论调用者的线程模型如何。
在客户端和进程外对象的交互是简单的,甚至当它们使用不容的线程模型,因为客户端和对象在不同的进程中。插入在客户端和服务端的COM组件使用标准封送和RPC可以提供线程模型互操作的代码。例如,一个单线程对象同时被多个自由线程客户端调用,则通过放置相应的窗口消息在服务器的消息队列中,调用将由COM组件同步。在每次检索和调度信息的时候对象套间将会收到一个调用。然而,必须注意确保进程内的服务器与客户端正确的交互。(参考In-Process Server Threading Issues)
在使用多线程模型进行编程时,最重要的问题是使您的代码成为线程安全的,以便用于特定线程的消息仅转到该线程,并且保护对线程的访问。