[多线程] 临界区Critical Section、互斥锁Mutex / 读写锁Read/Write Lock、事件Evetn、条件变量Condition

您所在的位置:网站首页 多线程只读需要锁吗 [多线程] 临界区Critical Section、互斥锁Mutex / 读写锁Read/Write Lock、事件Evetn、条件变量Condition

[多线程] 临界区Critical Section、互斥锁Mutex / 读写锁Read/Write Lock、事件Evetn、条件变量Condition

2024-07-17 14:28| 来源: 网络整理| 查看: 265

线程同步的方式和机制有临界区、互斥区、事件、信号量四种方式: 临界区(Critical Section)、互斥量(Mutex)、信号量(Semaphore)、事件(Event) ---------------------主要功能--------------------- 1、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。在任意时刻只允许一个线程对共享资源进行访问,如果有多个线程试图访问公共资源,那么在有一个线程进入后,其他试图访问公共资源的线程将被挂起,并一直等到进入临界区的线程离开,临界区在被释放后,其他线程才可以抢占。 2、互斥量:采用互斥对象机制。 只有拥有互斥对象的线程才有访问公共资源的权限,因为互斥对象只有一个,所以能保证公共资源不会同时被多个线程访问。互斥不仅能实现同一应用程序的公共资源安全共享,还能实现不同应用程序的公共资源安全共享 3、信号量:它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目 4、事 件: 通过通知操作的方式来保持线程的同步,还可以方便实现对多个线程的优先级比较的操作 ---------------------如何使用---------------------

 1、并行方式的信号量在访问相同的一组资源时是最好的方法,因为它最大限度减少了系统调度线程的成本。

 2、临界区和互斥量只应用于访问串行资源(例如使用全局计数器,系统参数访问和修改)。同一进程下的线程串行化时,只应该使用临界区。

 3、按指定的规则进行线程协调时使用事件。

------------------------------------------------------------------------------------------------------------------------------

 一、临界区:每个进程中访问临界资源的那段代码称为临界区(Critical Section),指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段,而这些共用资源又无法同时被多个线程访问的特性。当有线程进入临界区段时,其他线程或是进程必须等待(例如:bounded waiting 等待法),有一些同步的机制必须在临界区段的进入点与离开点实现,以确保这些共用资源是被互斥获得使用,例如:semaphore。只能被单一线程访问的设备,例如:打印机。多个进程中涉及到同一个临界资源的临界区称为相关临界区。

通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。

注意:虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。

1、临界区只能用于对象在同一进程里线程间的互斥访问;互斥体可以用于对象进程间或线程间的互斥访问。 2、临界区是非内核对象,只在用户态进行锁操作,速度快;互斥体是内核对象,在核心态进行锁操作,速度慢。 3、临界区和互斥体在Windows平台都下可用;Linux下只有互斥体可用。 4、临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。 5、互斥量:为协调共同对一个共享资源的单独访问而设计的

#include //临界区要用到的5种状态 CRITICAL_SECTION cs;//定义临界区对象 InitializeCriticalSection(&cs);//初始化临界区 EnterCriticalSection(&cs);//进入临界区 LeaveCriticalSection(&cs);//离开临界区 DeleteCriticalSection(&cs);//删除临界区 //临界区使得线程串行访问代码段资源,以此达到用原子方式操作共享资源的目的 // 临界区结构对象 CRITICAL_SECTION g_cs; // 共享资源 char g_cArray[10]; UINT ThreadProc10(LPVOID pParam) { // 进入临界区 EnterCriticalSection(&g_cs); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[i] = a; Sleep(1); } // 离开临界区 LeaveCriticalSection(&g_cs); return 0; } UINT ThreadProc11(LPVOID pParam) { // 进入临界区 EnterCriticalSection(&g_cs); // 对共享资源进行写入操作 for (int i = 0; i < 10; i++) { g_cArray[10 - i - 1] = b; Sleep(1); } // 离开临界区 LeaveCriticalSection(&g_cs); return 0; } …… void CSample08View::OnCriticalSection() { // 初始化临界区 InitializeCriticalSection(&g_cs); // 启动线程 AfxBeginThread(ThreadProc10, NULL); AfxBeginThread(ThreadProc11, NULL); // 等待计算完毕 Sleep(300); // 报告计算结果 CString sResult = CString(g_cArray); AfxMessageBox(sResult); }

二、互斥锁 / 体 Mutex,读写锁Read/Write Lock

  为协调共同对一个共享资源的单独访问而设计的。C++11中的lock_guard和unique_lock已在上篇文章中有介绍,此处主要介绍原生态的Mutex用法。

#include //是否命名互斥锁:若第三个参数传入非空字符串则次锁已命名,当前进程和其它进程都可以访问 HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes,          // 指向安全属性的指针NULL BOOLb InitialOwner, // 初始化互斥对象的所有者false,是否被当前线程持有 LPCTSTR lpName // 指向互斥对象名的指针nullptr ); DWORD WINAPI WaitForSingleObject(             //****可封装成Lock函数****可以让一个线程拥有互斥锁 __in HANDLE hHandle, //互斥量对象句柄 __in DWORD dwMilliseconds //等待时间 ); /*功能:当前线程释放互斥锁/不持有,以便让其他线程持有**返回值:BOOL,TRUE表示成功,FALSE表示失败。 **参数表:hMutex:HANDLE,指定一个互斥体的句柄。 */ BOOL WINAPI ReleaseMutex(HANDLE hMutex);        //****可封装成UnLock函数****

/*功能:当程序不需要互斥锁时,销毁**参数: hObject 代表一个已打开对象handle。 **返回值: **TRUE:执行成功; **FALSE:执行失败,可以调用GetLastError()获知失败原因。*/ BOOL CloseHandle(HANDLE hObject);

——> > > CRITICAL_SECTION和Mutex性能: 

1、在同一个进程的多线程同步锁,宜用临界区锁,它比较节约线程上下文切换带来的系统开销。但因临界区工作在用户模式下,所以不能对不同进程中的多线程进行同步。

2、因互斥对象锁属于内核对象,所以在进行多线程同步时速度会比较慢,但是可以在不同进程的多个线程之间进行同步。

——> > > Windows平台下的读写锁:在Vista和Server2008平台使用Visual studio 2005以后版本才能提供读写锁API,即SRW系列函数(InitializeSRWLock, AcquireSRWLockShared, AcquireSRWLockExclusive等)。

  读写锁在对资源进行保护的同时,还能区分想要读取资源值的线程(读取者线程)和想要更新资源的线程(写入者线程)。对于读取者线程,读写锁会允许他们并发的执行。当有写入者线程在占有资源时,读写锁会让其它写入者线程和读取者线程等待。

SRWLOCK srw; InitializeSRWLock(&srw); //*******写入线程可调用接口*********// //一旦初始化完成,就可以对=写入线程=调用AcquireSRWLockExclusive()函数和ReleaseSRWLockExclusive()函数 AcquireSRWLockExclusive(&srw); //...写入数据,写入东西的时候该线程独占,其他任何线程不可进入 ReleaseSRWLockExclusive(&srw); //********只读线程可调用接口*******// //对于只读线程可以调用AcquireSRWLockShared()函数和ReleaseSRWLockShared()函数,如下 AcquireSRWLockShared(&srw); //..读取数据,如果这时没有写入数据则多个读取线程可以进行 ReleaseSRWLockShared)&srw);

  不存在用来删除或销毁SRWLOCK的函数,系统会自动执行清理工作。

  SRWLock和CRITICAL_SECTION功能差不多,不同的是由开发者控制读写线程,读线程里可同时读取,写线程执行则其它线程挂起,写完后才可以读取。同时SRWLock缺乏:

1) 不存在TryEnter(Shared / Exclusive)SRWLock 之类的函数:如果锁已经被占用,那么调用AcquireSRWLock(Shared/Exclusive) 会阻塞调用线程。

2) 不能递归地调用SRWLOCK。也就是说,一个线程不能为了多次写入资源而多次锁定资源,然后再多次调用ReleaseSRWLock来释放对资源的锁定。

  如果希望在应用程序中得到最佳性能,那么首先应该尝试不要共享数据,然后依次使用volatile读取,volatile写入,Interlocked API,SRWLock以及关键段。当且仅当所有这些都不能满足要求的时候,再使用内核对象。因为每次等待和释放内核对象都需要在用户模式和内核模式之间切换,这种切换的CPU开销非常大。

三、事件Event,类似于软终端,用来通知线程有一些事件已发生,从而启动后继任务的开始。    

  当一个手动复原的事件对象的状态被置为有信号状态时,该对象将一直保持有信号状态,直至明确调用ResetEvent函数将其置为无信号状态。当事件对象被设置为有信号状态时,任何数量的等待线程或者随后等待的线程都会被释放。当一个自动复原事件对象的状态被设置为有信号状态时,该对象一直保持有信号状态,直至一个单等待线程被释放;系统然后会自动重置对象到无信号状态。

 

  事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。 

//信号量包含的几个操作原语: HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, //TRUE需手动ResetEvent,FALSE线程释放自动重置 BOOL bInitialState, //指定事件对象初始状态TRUE有信号FALSE无信号 LPCSTR lpName ); //创建一个信号量 OpenEvent(); //打开一个事件 SetEvent() ; //【手动】回置事件 WaitForSingleObject() //等待一个事件 WaitForMultipleObjects(); //等待多个事件 WaitForMultipleObjects(); //函数原型: WaitForMultipleObjects( IN DWORD nCount, // 等待句柄数      IN CONST HANDLE *lpHandles, //指向句柄数组      IN BOOL bWaitAll, //是否完全等待标志      IN DWORD dwMilliseconds //等待时间 );

  参数nCount指定了要等待的内核对象的数目,存放这些内核对象的数组由lpHandles来指向。fWaitAll对指定的这nCount个内核 对象的两种等待方式进行了指定,为TRUE时当所有对象都被通知时函数才会返回,为FALSE则只要其中任何一个得到通知就可以返回。 dwMilliseconds在这里的作用与在WaitForSingleObject()中的作用是完全一致的。如果等待超时,函数将返回 WAIT_TIMEOUT。

#include #include #include using namespace std; #define EventName "eventName" int main(int argc, char* argv[]) { //初始化时无信号量 HANDLE handle = CreateEvent(NULL,FALSE, FALSE,EventName); if (handle != NULL) { int count = 0; while (count < 10) { //启动此进程,一直等待 WaitForSingleObject(handle,INFINITE); cout


【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3