Windows 平臺下的同步機制 (2)– 互斥體(Mutex)

Windows 平臺下的同步機制 (2)– 互斥體(Mutex)

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 <windows.h>
#include <process.h>
#include <iostream>
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" <<endl;
    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;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章