Windows线程同步——互斥量对象 雨点打透心脏的1/2处 2022-06-14 10:24 295阅读 0赞 ## 1. 概述 ## 当两个或更多线程需要同时访问一个共享资源时,为了保证程序的正常运行,需要保证同一个资源在同一时刻只能有一个线程去访问它。Mutex 是同步基元,它只向一个线程授予对共享资源的独占访问权。如果一个线程获取了互斥体,则要获取该互斥体的第二个线程将被挂起,直到第一个线程释放该互斥体。 互斥量与关键段(线程同步方式)的行为完全相同,当互斥量是内核对象,而关键段是用户模式下的的同步对象。互斥量对象包含: 一个线程 ID ,使用计数和递归计数 。线程 ID 表示当前占用该互斥量的线程 ID ,递归计数表示该线程占用互斥量的次数,使用计数表示使用互斥量对象的不同线程的个数。 互斥量对象有许多用途,它是使用最为频繁的内核对象之一,一般用来对多个进程访问同一块内存进行同步。 ## 2. 互斥量相关API ## ### 2.1 CreateMutex()函数 ### HANDLE WINAPI CreateMutex( _In_opt_ LPSECURITY_ATTRIBUTES lpMutexAttributes, _In_ BOOL bInitialOwner, _In_opt_ LPCTSTR lpName ); 参数说明 **lpMutexAttributes:**互斥量安全访问属性,一般置为 NULL ; **bInitialOwner:**控制互斥量的初始状态,一般设置为 false ,是互斥量的线程 ID 和递归计数都设为 0 ,表示互斥量不被任何进程占用,处于触发状态,其他进程可以进行调用等待函数,获得该互斥量对象,获得对同步资源的占用。如果初始值设为 true ,互斥量的线程 ID 设置为当前线程,递归计数设为 1 ,表示当前线程占用互斥量对象,拥有对同步资源的独占,互斥量处于未触发状态。 **lpName:**用于创建有名的内核对象,即用来创建跨进程边界的内核对象。 CreateMutex 用于创建名为 lpName 的互斥量内核对象,并返回指向该内核对象的句柄。如果该内核对象已经存在,那么 CreateMutex 会返回该内核对象的句柄,并通过系统返回错误 ERROR\_ALREADY\_EXISTS ,通过 GetLastError ()获得。 ### 2.2 OpenMutex()函数 ### HANDLE WINAPI OpenMutex( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ LPCTSTR lpName ); 参数说明 **dwDesiredAccess:**对互斥量对象访问权限的设置, MUTEX\_ALL\_ACCESS 请求对互斥体的完全访问, MUTEX\_MODIFY\_STATE 允许使用 ReleaseMutex 函数, SYNCHRONIZE 允许互斥体对象同步使用; **bInheritHandle:**是否希望子进程继承互斥量对象的句柄,一般设置为false ; **lpName:**要打开的互斥量对象的名称; OpenMutex 用于打开已经存在的互斥量对象,若打开成功,则返回指向该内核对象的句柄,否则返回 NULL 。可以使用 CreateMutex 来实现打开功能。 ### 2.3 WaitForSingleObject()函数 ### DWORD WINAPI WaitForSingleObject( _In_ HANDLE hHandle, _In_ DWORD dwMilliseconds ); 参数说明 **hHandle :**指向内核对象的句柄; **dwMilliseconds:**线程最大等待多长时间,直到该对象被触发。经常使用INFINITE ,表示阻塞等待。 WaitForSingleObject被称呼为等待函数,是等待内核对象被触发通用的等待函数,被用在所有的内核对象触发等待中。等待函数在等待互斥量内核对象时,会进行互斥量的线程ID 是否为 0 ,如果为非 0 ,表示互斥量处于未触发状态,等待函数会被阻塞。当另外一个线程将互斥量释放,使其线程 ID 为 0 时,系统会唤醒阻塞的等待函数,把互斥量的线程 ID 设置为它的线程 ID ,使其成为可调度状态 。 还有一个WaitForMultipleObject函数,用于等待多个内核对象被触发。 ### 2.4 ReleaseMutex() 函数 ### BOOL WINAPI ReleaseMutex( _In_ HANDLE hMutex ); 参数说明 **hMutex:**互斥量内核对象的句柄; 当一个线程访问完通过互斥量对象获得的独占资源后,应该调用ReleaseMutex,使互斥量恢复为未触发状态。即设置互斥量对象的线程ID 和递归计数为 0 ,当递归计数大于 1 时,还有进行对应多次的 ReleaseMutex 。 ## 3. 示例代码 ## DWORD WINAPI my_thread1(LPVOID m_pParameter); //用户线程1 DWORD WINAPI my_thread2(LPVOID m_pParameter); //用户线程2 UINT count = 0; //全局的计数函数 HANDLE m_hMutex; //定义一个互斥量句柄 int _tmain(int argc, _TCHAR* argv[]) { system("color f0"); int count_size(50); m_hMutex = ::CreateMutex(NULL, false, NULL); //初始化互斥量 HANDLE m_h1 = CreateThread(NULL, NULL, my_thread1, &count_size, NULL, NULL); //用户线程1 HANDLE m_h2 = CreateThread(NULL, NULL, my_thread2, &count_size, NULL, NULL); //用户线程2 ::WaitForSingleObject(m_h1, INFINITE); //等待子线程结束 ::WaitForSingleObject(m_h2, INFINITE); //等待子线程结束 ::CloseHandle(m_hMutex); //关闭句柄 ::CloseHandle(m_h1); ::CloseHandle(m_h2); system("pause"); return 0; } //用户线程1 DWORD WINAPI my_thread1(LPVOID m_pParameter) { UINT* my_count = (UINT*)m_pParameter; cout << "thread1" << endl; for (int i = 0; i < *my_count; ++i) { ::WaitForSingleObject(m_hMutex, INFINITE); //等待互斥量 count = i; cout << count << "\t"; ::ReleaseMutex(&m_hMutex); //释放互斥量 } return 0; } //用户线程2 DWORD WINAPI my_thread2(LPVOID m_pParameter) { UINT* my_count = (UINT*)m_pParameter; cout << "thread2" << endl; for (int i = 0; i < *my_count; ++i) { ::WaitForSingleObject(m_hMutex, INFINITE); //等待互斥量 count = i; cout << count << "\t"; ::ReleaseMutex(&m_hMutex); //释放互斥量 } return 0; }
还没有评论,来说两句吧...