一、什么是线程同步
线程同步包含线程同步与线程互斥。
线程同步:同步就是协同步调,按预定的先后顺序进行运行。线程同步是指多线程通过特定的设置(如:信号量、事件对象、临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系。
线程互斥:线程互斥是指对于共享的进程系统资源,在各个单位线程访问时的排他性,当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其他使用该资源的线程必须等待,直到占用资源者释放该资源,线程互斥可以看成时一种特殊的线程同步。
二、线程同步的方式和机制
临界区、互斥对象:主要用于互斥控制,都具有拥有权的控制方法,只有拥有该对象的线程才能执行任务,所以拥有,执行完任务后一定要释放该对象。
信号量、事件对象:事件对象是以通知的方式进行控制的,主要用于同步控制。
1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问,在任意时刻只允许一个线程对共享资源访问,如果有多个线程试图访问公共资源,那么在一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后其他线程才可以抢占。
2、互斥对象:互斥对象和临界区很像,采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程同时访问,当前拥有拥有互斥对象的线程处理完任务后,必须将线程交出,以便其他线程访问。
*总结互斥量Mutex:
1>、互斥量是内核对象,它与关键段都有“线程所有权”,故不能用于线程同步;
2>、互斥量能够用于多个进程之间的线程互斥问题
3、互斥锁:对于多线程的程序的程序,访问冲突的问题是很普遍的,解决的办法是引入互斥锁,获得锁的线程可以完成“读----修改----写”的操作,然后释放锁给其他线程,没有获得锁的线程只能等待而不能访问共享数据,这样,“读----修改----写”的操作组成一个原子操作,要么都执行,要么都不执行,不会执行到中间被打断,也不会在其他处理器上并行作这个操作。
4、条件变量:线程间的同步还有这样一情况:线程A需要等待某个条件成立,才能继续往下执行,现在这个条件不成立了,线程A就阻塞等待,而线程B在执行过程中使这个条件成立的,就唤醒了线程A继续执行,在pthread库中通过条件变量来阻塞等待一个条件,或者唤醒等待这个条件的线程。
5、信号量:Mute变量是非0即1的,可看作一种资源的可用数量,初始化时Mutex是1,表示有一个可用资源,加锁时获得该资源,将Mutex减为0,表示不再有可用资源,解锁时,Mutex重新加到1,表示又有了一个可用资源,信号量和Mutex类似,表示可用资源的数量,和Mutex不同的是这个数量可以大于1。
第一个CreateMutex
函数功能:创建互斥量(注意与事件Event的创建函数对比)
函数原型:
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner, LPCTSTR lpName);
函数说明:第一个参数表示安全控制,一般直接传入NULL。第二个参数用来确定互斥量的初始拥有者。如果
传入TRUE表示互斥量对象内部会记录创建它的线程的线程ID号并将递归计数设置为1,由于该线程ID非零,所以互
斥量处于未触发状态。如果传入FALSE,那么互斥量对象内部的线程ID号将设置为NULL,递归计数设置为0,这意
味互斥量不为任何线程占用,处于触发状态。第三个参数用来设置互斥量的名称,在多个进程中的线程就是通过名
称来确保它们访问的是同一个互斥量。函数访问值:成功返回一个表示互斥量的句柄,失败返回NULL。
第二个打开互斥量
函数原型:
HANDLE OpenMutex(DWORD dwDesiredAccess,BOOL bInheritHandle,LPCTSTR lpName //名称);
函数说明:第一个参数表示访问权限,对互斥量一般传入MUTEX_ALL_ACCESS。详细解释可以查看MSDN文档。
第二个参数表示互斥量句柄继承性,一般传入TRUE即可。第三个参数表示名称。某一个进程中的线程创建互斥量
后,其它进程中的线程就可以通过这个函数来找到这个互斥量。函数访问值:成功返回一个表示互斥量的句柄,失
败返回NULL。
第三个触发互斥量
函数原型:
BOOL ReleaseMutex (HANDLE hMutex);
函数说明:访问互斥资源前应该要调用等待函数,结束访问时就要调用ReleaseMutex()来表示自己已经结束
访问,其它线程可以开始访问了。
最后一个清理互斥量
由于互斥量是内核对象,因此使用CloseHandle()就可以(这一点所有内核对象都一样)。
首先我们需要创建CreateMutex一把互斥对象,我们可以指明当前线程是否拥有它,互斥对象完全就像一把钥匙一样,我们用WaitForSignalObject来等待这把钥匙,但是这把钥匙被等到并且使用后必须释放-----ReleaseMutex ,不然别人永远无法等到。这样从等待到释放中间的代码段永远都是只有一个线程在执行,也就形成了互斥控制。当然互斥对象的句柄是要关闭的CloseHandle。