• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • 經典線程同步 互斥量Mutex的使用分析

           互斥量(mutex)內核對象用來確保一個線程獨占對一個資源的訪問。

           互斥量對象包含一個使用計數、線程ID以及一個遞歸計數。

           互斥量與關鍵段的行為完全相同。但是,互斥量是內核對象,而關鍵段是用戶模式下的同步對象。

           線程ID用來標識當前占用這個互斥量的是系統中哪兒個線程,遞歸計數表示這個線程占用該互斥量的次數。

           他們一般用來對多個線程訪問的同一塊內存進行保護。如果多個線程要同時更新內存塊,那么其中的數據將遭到破壞。互斥量可以確保正在訪問內存塊的任何線程會獨占對內存塊的訪問權,這樣就保證了數據的完整性。

     

           互斥量的使用規則:

    1.如果線程ID為0 (無效線程ID),那么該互斥量不為任何線程所占用,它處于觸發狀態。

       可以理解為:無人使用,即觸發。

    2.如果線程ID為非零值,那么有一個線程已經占用了該互斥量,他處于未觸發狀態。

    3.與所有其他內核對象不同,操作系統對互斥量進行了特殊處理,允許他們違反一些常規的規則。

     

     

           互斥量也是一個內核對象,它用來確保一個線程獨占一個資源的訪問。互斥量與關鍵段的行為非常相似,并且互斥量可以用于不同進程中的線程互斥訪問資源。使用互斥量Mutex主要將用到四個函數。下面是這些函數的原型和使用說明。

     

    第一個 CreateMutex

    函數功能:創建互斥量(注意與事件Event的創建函數對比)

    函數原型:

    HANDLECreateMutex(

      LPSECURITY_ATTRIBUTES lpMutexAttributes,

      BOOL bInitialOwner,     

      LPCTSTR lpName

    );

    函數說明:

    第一個參數表示安全控制,一般直接傳入NULL。

    第二個參數用來確定互斥量的初始擁有者。

    如果傳入TRUE表示互斥量對象內部會記錄創建它的線程的線程ID號并將遞歸計數設置為1,由于該線程ID非零,所以互斥量處于未觸發狀態。

    如果傳入FALSE,那么互斥量對象內部的線程ID號將設置為NULL,遞歸計數設置為0,這意味互斥量不為任何線程占用,處于觸發狀態。

    第三個參數用來設置互斥量的名稱,在多個進程中的線程就是通過名稱來確保它們訪問的是同一個互斥量。

    函數訪問值:

    成功返回一個表示互斥量的句柄,失敗返回NULL。

     

    第二個打開互斥量

    函數原型:

    HANDLEOpenMutex(

     DWORD dwDesiredAccess,

     BOOL bInheritHandle,

     LPCTSTR lpName     //名稱

    );

    函數說明:

    第一個參數表示訪問權限,對互斥量一般傳入MUTEX_ALL_ACCESS。詳細解釋可以查看MSDN文檔。

    第二個參數表示互斥量句柄繼承性,一般傳入TRUE即可。

    第三個參數表示名稱。某一個進程中的線程創建互斥量后,其它進程中的線程就可以通過這個函數來找到這個互斥量。

    函數訪問值:

    成功返回一個表示互斥量的句柄,失敗返回NULL。

     

    第三個觸發互斥量

    函數原型:

    BOOLReleaseMutex (HANDLE hMutex)

    函數說明:

    訪問互斥資源前應該要調用等待函數,結束訪問時就要調用ReleaseMutex()來表示自己已經結束訪問,其它線程可以開始訪問了。

     

    最后一個清理互斥量

    由于互斥量是內核對象,因此使用CloseHandle()就可以(這一點所有內核對象都一樣)。

     

     

           在創建互斥量之后,為了獲得對被保護資源的訪問權,線程要調用一個等待函數并傳入互斥量的句柄。在內部,等待函數會檢查線程ID是否為0(互斥量處于觸發狀態)。如果為0,那么函數會把線程ID 設為調用線程的線程ID,把遞歸計數設為1,然后讓調用線程繼續運行。

           如果等待函數檢測到線程ID不為0(互斥量處于未觸發狀態),那么調用線程將進入等待狀態。當另一個線程將互斥量的線程ID設為0的時候,系統會記得有一個線程正在等待,于是它把線程ID設為正在等待的那個線程的ID,把遞歸計數設為1,使正在等待的線程變成可調度狀態。這些對互斥量內核對象的檢查和修改都是以原子方式進行的。

            即,等待函數檢測的是 線程ID, 而不是遞歸計數。

     

    在用來觸發普通內核對象和撤銷觸發普通內核對象的規則中,有一條不適用于互斥量。

     

           假設線程正在等待一個未觸發的互斥量對象,在這種情況下,線程通常會進入等待狀態。但是,

    系統會檢查 想要獲得互斥量的線程的線程ID 與 互斥量對象的內部記錄的線程ID 是否相同。

           如果線程ID一致,那么系統會讓線程保持可調度狀態——即使該互斥量尚未觸發。(線程所有權,可理解為同步時,無效)

           對于系統中的任何其他內核對象來說,我們都找不到這種“異常”的活動,

           每次線程成功的等待了一個互斥量,互斥量對象的遞歸計數會遞增,使遞歸計數大于1的唯一途徑就是利用這個異常,讓線程多次等待同一個互斥量。

           當目前占有訪問權的線程不再需要訪問資源的時候,它必須調用releasemutex函數來釋放互斥量。

           如果線程成功地等待了互斥量對象不止一次,需要調用ReleaseMutex 相同次數,才能使得對象的遞歸計數變成0。當遞歸計數變成0的時候,函數還會將線程ID設為0,這樣就觸發了對象。

     

    以下代碼中均忘記了關閉互斥量,大家寫的時候不要忘記。

    下面舉例:

    #include <iostream>
    #include <windows.h>
    #include <process.h>
    
    using namespace std;
    
    const int threadNum = 20;
    HANDLE mutex;
    volatile int number;
    
    unsigned int __stdcall threadFunc(PVOID pm) {
    	int th = * (int *)pm;
    	WaitForSingleObject(mutex, INFINITE);
    	number++;
    	ReleaseMutex(mutex);
    	return 0;
    }
    
    int main() {
    	HANDLE handle[threadNum];
    	mutex = CreateMutex(NULL, FALSE, NULL);
    	int i,n=threadNum;
    	while(n--) {
    		number = 0;
    		for(i=0; i<threadNum; i++) {
    			handle[i] = (HANDLE) _beginthreadex(NULL, 0, threadFunc, (PVOID) &i, 0, NULL);
    		}
    
    		WaitForMultipleObjects(threadNum, handle, TRUE, INFINITE);
    		cout << "線程個數為" << number << endl;
    	}
    
    	getchar();
    	return 0;
    }

    運行結果為:


     

    mutex = CreateMutex(NULL, FALSE, NULL);

    表示互斥量創建時,處于觸發狀態

    若將FALSE 改為 TRUE,運行效果如下:


    則沒有任何顯示,因為創建時 互斥量的線程ID是 主線程的線程ID, 遞歸計數為1,所以子線程處于等待狀態。

     

    下面例舉不適用互斥量的情況:

    一、

    #include <iostream>
    #include <windows.h>
    #include <process.h>
    
    using namespace std;
    
    const int threadNum = 40;
    HANDLE mutex;
    volatile int number;
    
    unsigned int __stdcall threadFunc(PVOID pm) {
    	int th = * (int *)pm;
    	Sleep(100);
    	number++;
    	Sleep(100);
    	ReleaseMutex(mutex);
    	return 0;
    }
    
    int main() {
    
    	HANDLE handle[threadNum];
    	mutex = CreateMutex(NULL, FALSE, NULL);
    
    	int i,n=threadNum;
    
    	while(n--) {
    		number=0;
    		for(i=0; i<threadNum; i++) {
    				handle[i] = (HANDLE) _beginthreadex(NULL, 0, threadFunc, (PVOID) &i, 0, NULL);
    				WaitForSingleObject(mutex, INFINITE);  // 等待函數檢測線程ID
    			}
    
    			WaitForMultipleObjects(threadNum, handle, TRUE, INFINITE);
    			cout << "線程個數為" << number << endl;
    	}
    	getchar();
    	return 0;
    }

    運行結果如下:


     

    因為線程所有權的概念,此時子線程之間 不是互斥的關系了。

     

    二、

    #include <iostream>
    #include <windows.h>
    #include <process.h>
    
    using namespace std;
    
    const int threadNum = 10;
    HANDLE mutex;
    volatile int number;
    
    unsigned int __stdcall threadFunc(PVOID pm) {
    	int th = * (int *)pm;
    	number++; //線程不安全
    	return 0;
    }
    
    unsigned int __stdcall lastFunc(PVOID pm) {
    	WaitForSingleObject(mutex, INFINITE);
    	cout << "hello world" << endl;
    	Sleep(100);
    	ReleaseMutex(mutex);
    	return 0;
    }
    
    int main() {
    	HANDLE handle[threadNum],last_handle;
    	mutex = CreateMutex(NULL, FALSE, NULL);
    	int i,n=threadNum;
    
    	for(i=0; i<threadNum; i++) {
    		handle[i] = (HANDLE) _beginthreadex(NULL, 0, threadFunc, (PVOID) &i, 0, NULL);
    		WaitForSingleObject(mutex, INFINITE);  // 等待函數檢測線程ID
    	}
    
    	WaitForMultipleObjects(threadNum, handle, TRUE, INFINITE);
    	cout << "線程個數為" << number << endl;
    
    	last_handle = (HANDLE) _beginthreadex(NULL, 0, lastFunc, NULL, 0, NULL);
    
    	Sleep(1000);  //保證在回收mutex前 執行完last_handle,否則互斥量將不起作用
    	CloseHandle(mutex);
    	
    	getchar();
    	return 0;
    }

    運行效果如下:


    由于互斥量未釋放  導致last_handle線程,等待。

     

    正確釋放mutex

    WaitForMultipleObjects(threadNum, handle, TRUE, INFINITE);之前,加入

    注意,釋放mutex必須在其占用的線程當中進行釋放,在其他線程中釋放將沒有任何效果。

    for(i=0; i<threadNum; i++) {
    		ReleaseMutex(mutex);
    	}

    運行效果如下:


     

    正常。

     

     

    版權聲明:本文為iteye_10289原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/iteye_10289/article/details/82547804

    智能推薦

    【Linux】深入理解線程(線程同步、互斥量mutex、死鎖、讀寫鎖、條件變量、信號量)

    一、同步概念        1、線程同步:             同步即協同步調,按預定的先后次序運行。             線程同步,只一個線程發出某一功能調用時,在沒有得到結果之前,該調用不返回。同時,其他線程為保證數據一致性,不...

    Linux線程同步---互斥量

    在日常生活中,為了避免在火車站、電影院排隊購票,網上購買火車票、電影票也越來越普遍。我們首先實現一個購票系統,每當有人購買一張票的時候,總票數就會減1,而此時的“總票數”就是一個共享變量。 代碼實現簡單的網上購票系統: 通過輸出的結果我們發現和預期不相同,原因是我們操作了多個線程的共享變量ticket,if語句條件為真時,進程可以并發執行,而usleep模擬的是售票時對總票...

    線程同步之互斥量

    1.互斥量 采用互斥對象機制。 只有擁有互斥對象的線程才有訪問公共資源的權限,因為互斥對象只有一個,所以能保證公共資源不會同時被多個線程訪問。互斥不僅能實現同一應用程序的公共資源安全共享,還能實現不同應用程序的公共資源安全共享。 2.使用 3.實例 4.結果輸出...

    線程同步圖解 - 互斥量

    線程同步問題廣泛應用于多種場景下,特別是與網絡數據收發等耗時操作有關的場景。線程的操作往往比較抽象,且大多運行在程序的后臺,無法直觀的觀察其運行狀態,因此,本文以圖解的形式,為讀者講述線程同步的原理,并附以相關例程方便大家調試。 本文講述互斥量(也叫互斥鎖pthread_mutex_t)線程同步原理,互斥量作為比較簡單的線程同步方式,在實際的并發問題中會經常碰到。 更多線程同步的例程見[公眾號:斷...

    Linux多線程——使用互斥量同步線程

    前文再續,書接上一回,在上一篇文章:Linux多線程——使用信號量同步線程中,我們留下了一個如何使用互斥量來進行線程同步的問題,本文將會給出互斥量的詳細解說,并用一個互斥量解決上一篇文章中,要使用兩個信號量才能解決的只有子線程結束了對輸入的處理和統計后,主線程才能繼續執行的問題。 一、什么是互斥量 互斥量是另一種用于多線程中的同步訪問方法,它允許程序鎖住某個對象,使得每次只...

    猜你喜歡

    多線程(2)---(同步&互斥&互斥量)

        上一個博客我們學習了線程的創建,如如何查看線程ID,線程與進程之間的區別等等,可以參考上篇博客線程(1)。而這一篇博客我們繼續來學習線程,本篇博客我們的目標是: 1、學會線程同步 2、學會使用互斥量、條件變量 線程同步與互斥 mutex(互斥量)     大部分情況下,線程使用的數據都是局部變量,變量的地址空間在棧空間內,這種情況...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    精品国产乱码久久久久久蜜桃不卡