C++进阶—>线程同步随笔

       线程同步主要有五种方法:原子访问,临界区,信号量,事件和互斥量;其中原子访问和临界区属于用户模式的同步;信号量,事件和互斥量属于内核模式的同步。

       原子访问是通过将共享资源设置为原子变量,当一个线程访问的时候,其余的线程不得访问。C++使用临界区,信号量,事件和互斥量实现线程同步的时候,即定义相应的同步控制对象句柄,通过获取控制对象是否空闲来决定是否执行线程,若控制对象空闲则获取该对象执行线程,执行完毕后释放该控制对象,若控制对象不空闲,则等待,直到控制对象空闲并获取到控制对象后才执行线程。

       下面简单谈一谈临界区,信号量,事件和互斥量四种方法实现多线程同步或顺序执行的大体思路(理解未必深而透彻,初步理解有待提高仅作为参考)

       对于使用Windows API实现线程同步来说,只是创建了临界区或信号量或事件或互斥量控制对象,使用WaitForSingleObject函数来等待控制对象是否空闲,若空闲则获取其控制对象执行自己的线程函数,若不空闲则持续等待或等待固定时间,由于多线程是通过时间片轮转机制实现的,通过一个控制对象仅仅实现了多线程的同步(不同时访问某个资源或重复执行某条语句),但并没有实现多线程的顺序执行(即线程1、线程2、线程3....等按照顺序执行),对于多线程的顺序执行仅仅创建一个控制对象是不足以满足顺序执行,下面以事件实现三个线程顺序执行阐述:

       创建三个事件控制对象,CreatEvent event1,event2,event3;

线程1监听event1,执行完后释放event1,设置event2为有信号状态;

线程2监听event2,执行完后释放event2,设置event3为有信号状态;

线程3监听event3,执行完后释放event3,设置event1为有信号状态;

依次执行即可实现多线程的顺序执行。

/************使用临界区对象实现多线程同步***************
临界区被初始化后,当程序进入临界区后便拥有临界区的所有权,其余线程无权进入只能等对方释放临界区之后,方可进入临界区拥有其所有权再对临界区进行操作
InitializeCriticalSection()初始化临界区;
EnterCriticalSection()进入临界区;
LeaveCriticalSection()释放临界区所有权并离开临界区;
注意:上述是windows API中相关函数,CCriticalSection类是MFC中定义的临界区类,需要在MFC程序中使用(此程序为控制台程序无法使用MFC类),可以操作临界区,lock锁定临界区、unlock释放临界区
	  临界区为依次访问,不能实现其中一个线程一释放临界区就会被另一个线程访问临界区!不能实现实时监听
********************************************************/

/************使用事件对象实现多线程同步***************
事件对象是指用户在程序中使用内核对象的有无信号状态实现线程的同步临界区被初始化后,当程序进入临界区后便拥有临界区的所有权,其余线程无权进入只能等对方释放临界区之后,方可进入临界区拥有其所有权再对临界区进行操作
CreatEvent()创建并返回事件对象;
SetEvent()将指定的事件设置为有信号状态(有信号状态下其余线程可以访问);
ResetEvent()将指定的事件设置为无信号状态;
除SetEvent函数外,WaitForSingleObject函数等待指定事件。
注意:上述是Windows API函数,CEvent类是MFC实现事件对象的类
	  事件对象为立即访问,一旦事件对象被设置为有信号 立刻会被其余线程访问!能实现实时监听
********************************************************/

/************使用互斥对象实现多线程同步***************
互斥对象还可以在进程间使用,在实现线程同步时包含一个线程ID和一个计数器,线程ID表示拥有互斥对象的线程,计数器表示该互斥对象被同一线程所使用次数
CreatMutex()创建并返回互斥对象;
ReleaseMutex()释放互斥对象句柄;
WaitForSingleObject()对该对象进行请求。
注意:上述是Windows API函数,CMutex类是MFC中的互斥对象类
	  互斥对象为立即访问,一旦互斥对象被释放 立刻会被其它正在等待的线程访问!能实现实时监听
********************************************************/

/************使用信号量实现多线程同步***************
信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。
它指出了同时访问共享资源的线程 最大数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。
在用CreateSemaphore()创建信号量 时即要同时指出允许的最大资源计数和当前可用资源计数。
一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数 就会减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。
但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目, 不能在允许其他线程的进入,此时的信号量信号将无法发出。
线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可 用资源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。

CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,LONG lMaximumCount,LPCTSTR lpName) 创建一个信号量  
第一个参数表示安全控制,一般直接传入NULL;
第二个参数表示初始化可用资源数量;
第三个参数表示最大并发数量;
第四个参数表示信号量的名称,传入NULL表示匿名信号量。

OpenSemaphore(DWORD dwDesiredAccess,BOOL bInheritHandle,LPCTSTR lpName) 打开一个信号量  
第一个参数表示访问权限,SEMAPHORE_ALL_ACCESS要求对事件对象的完全访问;SEMAPHORE_MODIFY_STATE 允许使用ReleaseSemaphore函数;SYNCHRONIZE允许同步使用信号机对象。
第二个参数表示信号量句柄继承性,一般传入TRUE即可。
第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。

ReleaseSemaphore(HANDLE hSemaphore,LONG lReleaseCount, LPLONG lpPreviousCount) 释放信号量 
第一个参数是信号量的句柄。
第二个参数表示增加个数,必须大于0且不超过最大资源数量。
第三个参数可以用来传出先前的资源计数,设为NULL表示不需要传出

注意:在CreateSemaphore创建信号量的时候如果第二个参数设置为0则表示可用的信号量资源为0,使用WaitForSingleObject函数无法获取到信号量(因为无可用的资源),此时需要调用OpenSemaphore函数打开信号量,使其可用资源为最大;
	  若不想使用OpenSemaphore函数 则在创建信号量的时候不要将初始化可用资源设置为0即可。
	  信号量机制:在信号量未达到最大并发数的时候,各线程可以同时获取信号量,直到达到了最大并发数后续线程不可再获取信号量,需要使用ReleaseSemaphore函数将当前线程的信号量释放并将信号量可用资源数+1,后续线程可获取信号量执行。
********************************************************/


©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页