进程
一个正在执行的程序
计算机中正在运行的程序的一个实例
可以分配给处理器并且由处理器执行的一个实体
由一个顺序执行的代码段、一个当前状态和一组相关系统资源所刻画的活动单元
进程是操作系统资源分配的基本单位
线程
进程中代码执行的一个序列
线程是操作系统可以进行运算调度的基本单位
进程、线程的区别
1. 划分尺度不同。线程是进程的一部分。一个进程至少包含一个线程,可以有多个线程。
2. 资源分配不同。进程是资源分配的基本单位,同一进程的线程共享其进程的代码、数据及文件等资源,而线程仅拥有自己的寄存器和栈,除了CPU外线程与任何系统资源分配都无关。
3. 地址空间不同。系统在运行时会为每个进程分配内存空间,进程有独立的地址空间,但线程没有,多个线程共享内存。
4. 执行过程不同。每个独立的线程有一个程序运行的入口、顺序执行序列和程序出口。但线程却不能独立执行,需要依附于应用程序。
同步
同步是指线程之间的一种制约关系,线程A的执行需要线程B的消息,在没有收到线程B的消息时应等待,直到消息到达。
事件
事件是一个内核对象,可以跨进程使用。
事件被分为:手动置位和自动置位。手动置位是同时向所有等待线程发信号通知,某一操作已经做完了,使他们都处于有信号的状态,都成为可调度线程;自动置位是向某一线程发信号,使它为有信号状态,成为可调度线程。
创建事件
/*
lpEventAttributes 事件的安全级别,设为NULL为默认设置
bManualReset 人工置位为true,自动置位为false
bInitialState 事件初始化的状态,有状态为true,无状态为false
lpName 事件的名称,设为NULL则为匿名
*/
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,
BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);
BOOL SetEvent(HANDLE hEvent);
对于手动置位的事件,SetEvent方法将解锁在该事件上等待的所有线程,即当人工重置变为有信号时,所有的等待该事件的线程都变为可调度线程,将保持有信号状态,直到某事件手动置位为无状态。对于自动置位的事件,SetEvent方法将仅解锁一个线程,通过调用WaitForSingleObject()自动调用Reset()方法使事件变为无信号状态。
BOOL ResetEvent(HANDLE hEvent);
信号量
信号量是内核对象,可用于进程间的同步也可用于同一进程中的线程间的同步,用来对资源进行计数。当资源数大于零,信号量处于触发状态;当资源是等于零,信号量未触发。
创建信号量
/*
lpSemaphoreAttributes 信号量的安全属性,设为NULL则为默认设置
lInitialCount 初始资源数,必须大于等于零,小于最大资源数
lMaximumCount 最大资源数
lpName 信号量名称,设为NULL则为匿名信号量
*/
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);
打开一个已有信号量
/*
dwDesiredAccess 访问方式,一般使用SEMAPHORE_ALL_ACCESS
bInheritHandle 继承特性,一般使用true
lpName 信号量名称
*/
HANDLE OpenSemaphore(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName);
释放信号量
/*
hSemaphore 要释放的信号量句柄
lReleaseCount 释放的数量,即资源的增加量,必须大于零
lpPreviousCount 原先的计数值,设为NULL为不需要传入
*/
BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount);
互斥
对于共享的资源,每个进程访问时具有排他性。对于共享资源任何时刻仅允许一个线程访问,其他线程想要访问则必须等待,直到占用资源的线程释放了该资源。互斥可以认为是一种特殊的同步。
临界区
临界区保护一段代码不被多于一个线程访问。
临界区相关函数
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 初始化临界区
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 进入临界区
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 离开临界区
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 删除临界区
因为主线程拥有线程所有权,所以可以重复多次进入临界区,导致子线程在接受参数之前主线程可能已经修改了参数,而无法实现线程同步。
互斥量
互斥量是一个内核对象,用来确保资源仅被一个线程访问,可以跨进程访问。
创建互斥量
/*
lpMutexAttributes 安全控制,设为NULL则为默认设置
bInitialOwner 是否成为该互斥量的初始拥有者,设为TRUE为拥有,互斥量为无信号状态;设为FALSE为不拥有,该信号量不为任何线程占有,互斥量为有信号状态
lpName 互斥量名称
*/
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName);
打开互斥量
/*
dwDesiredAccess 访问方式,一般设为MUTEX_ALL_ACCESS
bInheritHandle 该进程的子进程能否继承该互斥量,一般设为TRUE
lpName 互斥量名称
*/
HANDLE OpenMutex(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName);
释放互斥量
BOOL ReleaseMutex(HANDLE hMutex);
互斥量同临界区一样,主线程拥有线程控制权,因此同样不能用于同步。
互斥量可以解决因线程意外终止而造成的遗弃现象。具体来说,如果一个线程在释放互斥量之前因某些意外终止,照理来说应该互斥量还要能够被其他等待的线程使用,否则会陷入无限的等待,这是不合理的。因此,在这样的情况下,系统会将互斥量的状态设为有信号状态,这样其他线程就可以使用了。
小结
事件:用于通知其他线程事件已发生,从而启动线程
信号量:可以对资源进行计数,允许多个线程共享资源,限制了同一时刻多个线程能访问的最大资源数。
临界区:在任一时刻仅允许一个线程使用临界区资源。四者中仅有临界区不是内核对象。适合于数据访问的控制。
互斥量:拥有互斥量才可以访问共享资源,因此共享资源不会被多个线程所共享。