Liping Zou bio photo

Liping Zou

An Android Developer

Email Twitter Instagram Github Stackoverflow

Overview

###进程

  • 一个正在执行的程序
  • 计算机中正在运行的程序的一个实例
  • 可以分配给处理器并且由处理器执行的一个实体
  • 由一个顺序执行的代码段、一个当前状态和一组相关系统资源所刻画的活动单元

进程是操作系统资源分配的基本单位

###线程

进程中代码执行的一个序列

线程是操作系统可以进行运算调度的基本单位

###进程、线程的区别

  1. 划分尺度不同。线程是进程的一部分。一个进程至少包含一个线程,可以有多个线程。
  2. 资源分配不同。进程是资源分配的基本单位,同一进程的线程共享其进程的代码、数据及文件等资源,而线程仅拥有自己的寄存器和栈,除了CPU外线程与任何系统资源分配都无关。
  3. 地址空间不同。系统在运行时会为每个进程分配内存空间,进程有独立的地址空间,但线程没有,多个线程共享内存。
  4. 执行过程不同。每个独立的线程有一个程序运行的入口、顺序执行序列和程序出口。但线程却不能独立执行,需要依附于应用程序。

###同步

同步是指线程之间的一种制约关系,线程A的执行需要线程B的消息,在没有收到线程B的消息时应等待,直到消息到达。

####事件

事件是一个内核对象,可以跨进程使用。

事件被分为:手动置位和自动置位。手动置位是同时向所有等待线程发信号通知,某一操作已经做完了,使他们都处于有信号的状态,都成为可调度线程;自动置位是向某一线程发信号,使它为有信号状态,成为可调度线程。

  • 创建事件
1
2
3
4
5
6
7
8
/*
	lpEventAttributes 事件的安全级别,设为NULL为默认设置
	bManualReset 人工置位为true,自动置位为false
	bInitialState 事件初始化的状态,有状态为true,无状态为false
	lpName 事件的名称,设为NULL则为匿名
*/
HANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,
                   BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName);
  • 设置事件为有状态
1
BOOL ResetEvent(HANDLE hEvent); 

###信号量

信号量是内核对象,可用于进程间的同步也可用于同一进程中的线程间的同步,用来对资源进行计数。当资源数大于零,信号量处于触发状态;当资源是等于零,信号量未触发。

  • 创建信号量
1
2
3
4
5
6
7
8
/*
	lpSemaphoreAttributes 信号量的安全属性,设为NULL则为默认设置
	lInitialCount 初始资源数,必须大于等于零,小于最大资源数
	lMaximumCount 最大资源数
	lpName 信号量名称,设为NULL则为匿名信号量
*/
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, 
LONG lInitialCount, LONG lMaximumCount, LPCTSTR lpName);
  • 打开一个已有信号量
1
2
3
4
5
6
/*
	dwDesiredAccess 访问方式,一般使用SEMAPHORE_ALL_ACCESS
	bInheritHandle 继承特性,一般使用true
	lpName 信号量名称
*/
HANDLE OpenSemaphore(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName);
  • 释放信号量
1
2
3
4
5
6
/*
	hSemaphore 要释放的信号量句柄
	lReleaseCount 释放的数量,即资源的增加量,必须大于零
	lpPreviousCount 原先的计数值,设为NULL为不需要传入
*/
BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount, LPLONG lpPreviousCount);

###互斥

对于共享的资源,每个进程访问时具有排他性。对于共享资源任何时刻仅允许一个线程访问,其他线程想要访问则必须等待,直到占用资源的线程释放了该资源。互斥可以认为是一种特殊的同步。

####临界区

临界区保护一段代码不被多于一个线程访问。

临界区相关函数

1
2
3
4
void InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 初始化临界区
void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 进入临界区
void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 离开临界区
void DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection); // 删除临界区

因为主线程拥有线程所有权,所以可以重复多次进入临界区,导致子线程在接受参数之前主线程可能已经修改了参数,而无法实现线程同步。

####互斥量

互斥量是一个内核对象,用来确保资源仅被一个线程访问,可以跨进程访问。

  • 创建互斥量
1
2
3
4
5
6
/*
        lpMutexAttributes 安全控制,设为NULL则为默认设置
        bInitialOwner 是否成为该互斥量的初始拥有者,设为TRUE为拥有,互斥量为无信号状态;设为FALSE为不拥有,该信号量不为任何线程占有,互斥量为有信号状态
        lpName 互斥量名称
*/
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName);
  • 打开互斥量
1
2
3
4
5
6
/*
        dwDesiredAccess 访问方式,一般设为MUTEX_ALL_ACCESS
        bInheritHandle 该进程的子进程能否继承该互斥量,一般设为TRUE
        lpName 互斥量名称
*/
HANDLE OpenMutex(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName);
  • 释放互斥量
1
BOOL ReleaseMutex(HANDLE hMutex);

互斥量同临界区一样,主线程拥有线程控制权,因此同样不能用于同步。

互斥量可以解决因线程意外终止而造成的遗弃现象。具体来说,如果一个线程在释放互斥量之前因某些意外终止,照理来说应该互斥量还要能够被其他等待的线程使用,否则会陷入无限的等待,这是不合理的。因此,在这样的情况下,系统会将互斥量的状态设为有信号状态,这样其他线程就可以使用了。

###小结

  • 事件:用于通知其他线程事件已发生,从而启动线程
  • 信号量:可以对资源进行计数,允许多个线程共享资源,限制了同一时刻多个线程能访问的最大资源数。
  • 临界区:在任一时刻仅允许一个线程使用临界区资源。四者中仅有临界区不是内核对象。适合于数据访问的控制。
  • 互斥量:拥有互斥量才可以访问共享资源,因此共享资源不会被多个线程所共享。