Windows 平台下的同步机制 (2)– 互斥体(Mutex)

    技术2022-07-03  117

    windows api中提供了一个互斥体,功能上要比临界区强大。Mutex是互斥体的意思,当一个线程持有一个Mutex时,其它线程申请持有同一个Mutex会被阻塞,因此可以通过Mutex来保证对某一资源的互斥访问(即同一时间最多只有一个线程访问)。 调用CreateMutex可以创建或打开一个Mutex对象,其原型如下

    HANDLE CreateMutex(   LPSECURITY_ATTRIBUTES lpMutexAttributes,   BOOL bInitialOwner,   LPCTSTR lpName );

    其中参数lpMutexAttributes用来设定Mutex对象的安全描述符和是否允许子进程继承句柄。bInitialOwner表明是否将Mutex的持有者设置为调用线程。lpName参数设置Mutex的名字,该名字区分大小写并不能包含"",最大长度为MAX_PATH,可设置为NULL表明该Mutex为匿名对象。 如果调用成功,则返回Mutex的句柄,否则返回NULL,如果lpName不为NULL且调用前同名的Mutex已被创建,则返回同名Mutex的句柄,此时调用GetLastError将返回ERROR_ALREADY_EXISTS,参数bInitialOwner将被忽略。

    还可以调用OpenMutex打开创建的非匿名Mutex,原型如下

    HANDLE OpenMutex(   DWORD dwDesiredAccess,   BOOL bInheritHandle,   LPCTSTR lpName );

    在成功创建或打开Mutex后,可以使用wait functions来等待并获取Mutex的持有权。

    下面的例子用来通过Mutex对象控制某一应用程序只运行一次

        int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)     {         HANDLE hMutex = CreateMutex(NULL, FALSE, "Mutex_Only_One_Instance_Allowed");         if (NULL == hMutex)         {             Error("Create mutex error.");             return -1;         }         DWORD dw = WaitForSingleObject(hMutex, 0);         if (WAIT_FAILED == dw)         {             Error("Wait for mutex error.");             CloseHandle(hMutex); // 释放句柄,当指向同一系统对象的所有句柄释放后,该对象将被删除。             return -1;         }         else if (WAIT_TIMEOUT == dw)         {             // 另外一个实例正在运行             CloseHandle(hMutex);             return 1;         }

            // 没有其它实例在运行,本实例将继续运行         // 在此实现必要的功能性代码,如创建窗口,进入消息循环         // ……………

            ReleaseMutex(hMutex); // 释放hMutex的持有权,注意这并不等同于删除Mutex对象         CloseHandle(hMutex);

            return 0;     }

    其中WaitForSingleObject是等待特定对象发出信号(signaled),而Mutex对象在没有任何线程持有时会发出信号。

    与临界区(critical section)有什么区别,为什么强大?它们有以下几点不一致: 1.critical section是局部对象,而mutex是核心对象。因此像waitforsingleobject是不可以等待临界区的。 2.critical section是快速高效的,而mutex同其相比要慢很多 3.critical section使用范围是单一进程中的各个线程,而mutex由于可以有一个名字,因此它是可以应用于不同的进程,当然也可以应用于同一个进程中的不同线程。 4.critical section 无法检测到是否被某一个线程释放,而mutex在某一个线程结束之后会产生一个abandoned的信息。同时mutex只能被拥有它的线程释放。下面举两个应用mutex的例子,一个是程序只能运行一个实例,也就是说同一个程序如果已经运行了,就不能再运行了;另一个是关于非常经典的哲学家吃饭问题的例子。

    互斥体通常用于多进程之间的同步问题

    程序运行单个实例: #include "stdafx.h" #include #include #include using namespace std;

    //当输入s或者c时候结束程序 void PrintInfo(HANDLE& h, char t) {     char c;     while (1)     {         cin >> c;         if (c == t)         {             ReleaseMutex(h);             CloseHandle(h);             break;         }         Sleep(100);     } } int main(int argc, char* argv[]) {     //创建mutex,当已经程序发现已经有这个mutex时候,就相当于openmutex     HANDLE hHandle = CreateMutex(NULL, FALSE, "mutex_test");     if (GetLastError() == ERROR_ALREADY_EXISTS)     {         cout << "you had run this program!" << endl;         cout << "input c to close this window" << endl;         PrintInfo(hHandle, ‘c’);         return 1;     }     cout << "program run!" << endl;     cout << "input s to exit program" <     PrintInfo(hHandle, ‘s’);     return 1; }

    封装:

    struct _Lock {     _Lock(HANDLE& mtx) : _mtx(mtx), _ret(WAIT_OBJECT_0)     {         if (_mtx != 0)             _ret = WaitForSingleObject(_mtx, 100000);     }     ~_Lock()     {         Release();     }

        void Release()     {         if (_mtx != 0 && _ret == WAIT_OBJECT_0)             ReleaseMutex(_mtx);     }     HANDLE& _mtx;     DWORD _ret; };

    哲学家吃饭问题:

    const int PHILOSOPHERS = 5;          //哲学家人数 const int TIME_EATING = 50;         //吃饭需要的时间 毫秒 HANDLE event[PHILOSOPHERS];    //主线程同工作线程保持同步的句柄数组 HANDLE mutex[PHILOSOPHERS];   //mutex数组,这里相当于公共资源筷子 CRITICAL_SECTION cs;                //控制打印的临界区变量

    UINT WINAPI ThreadFunc(void* arg) {     int num = (int)arg;     DWORD ret = 0;     while (1)     {         ret = WaitForMultipleObjects(2, mutex, TRUE, 1000);         if (ret == WAIT_TIMEOUT)         {             Sleep(100);             continue;         }         EnterCriticalSection(&cs);             cout << "philosopher " << num << " eatting" << endl;         LeaveCriticalSection(&cs);         Sleep(TIME_EATING);         break;     }     //设置时间为有信号     SetEvent(event[num]);     return 1; } int main(int argc, char* argv[]) {     HANDLE hThread;     InitializeCriticalSection(&cs);     //循环建立线程     for (int i = 0; i < PHILOSOPHERS; i++)     {         mutex[i] = CreateMutex(NULL, FALSE, "");         event[i] = CreateEvent(NULL, TRUE, FALSE, "");         hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadFunc, (void*)i, 0, NULL);         if (hThread == 0)         {             cout << "create thread " << i << "failed with code: "                 << GetLastError() << endl;             DeleteCriticalSection(&cs);             return -1;         }         CloseHandle(hThread);     }     //等待所有的哲学家吃饭结束     DWORD ret = WaitForMultipleObjects(PHILOSOPHERS, event, TRUE, INFINITE);     if (ret == WAIT_OBJECT_0)     {         cout << "all the philosophers had a dinner!" << endl;     }     else     {         cout << "WaitForMultipleObjects failed with code: " << GetLastError() << endl;     }     DeleteCriticalSection(&cs);     for (int j = 0; j < PHILOSOPHERS; j++)     {         CloseHandle(mutex[j]);     }     return 1; }

    Further reading:

    1.Windows 平台下的同步机制 (1)– 临界区(CriticalSection)

    2.Windows 平台下的同步机制 (2)– 互斥体(Mutex)

    3.Windows 平台下的同步机制 (3)– 事件(Event)

    4.Windows 平台下的同步机制 (4)– 信号量(Semaphore)

    5.《windows核心编程》学习笔记(一)内核对象

    Reference:

    1.http://www.chinaitpower.com/A200507/2005-07-27/176735.html

    2.http://www.cppblog.com/wangjt/archive/2008/01/30/42235.aspx


    最新回复(0)