1. 程式人生 > >多執行緒程式設計-互斥鎖

多執行緒程式設計-互斥鎖

1.引言:

互斥鎖,是一種訊號量,常用來防止兩個程序或執行緒在同一時刻訪問相同的共享資源。可以保證以下三點:

原子性:把一個互斥量鎖定為一個原子操作,這意味著作業系統(或pthread函式庫)保證瞭如果一個執行緒鎖定了一個互斥量,沒有其他執行緒在同一時間可以成功鎖定這個互斥量。

唯一性:如果一個執行緒鎖定了一個互斥量,在它解除鎖定之前,沒有其他執行緒可以鎖定這個互斥量。

非繁忙等待:如果一個執行緒已經鎖定了一個互斥量,第二個執行緒又試圖去鎖定這個互斥量,則第二個執行緒將被掛起(不佔用任何cpu資源),直到第一個執行緒解除對這個互斥量的鎖定為止,第二個執行緒則被喚醒並繼續執行,同時鎖定這個互斥量。

從以上三點,我們看出可以用互斥量來保證對變數(關鍵的程式碼段)的排他性訪問。

2.函式說明:

需要的標頭檔案:pthread.h
1
)初始化互斥鎖

函式原型:int  pthread_mutex_init(pthread_mutex_t *mp, const pthread_mutexattr_t *mattr)

引數說明:mp 互斥鎖地址    mattr  屬性通常預設 null

初始化互斥鎖之前,必須將其所在的記憶體清零。

如果互斥鎖已初始化,則它會處於未鎖定狀態。互斥鎖可以位於程序之間共享的記憶體中或者某個程序的專用記憶體中。

2)鎖定互斥鎖

函式原型:

int pthread_mutex_lock(pthread_mutex_t *mutex); #include pthread_mutex_t mutex; int ret; ret = pthread_ mutex_lock(&mp); /* acquire the mutex */

函式說明:

 pthread_mutex_lock() 返回時,該互斥鎖已被鎖定。呼叫執行緒是該互斥鎖的屬主。如果該互斥鎖已被另一個執行緒鎖定和擁有,則呼叫執行緒將阻塞,直到該互斥鎖變為可用為止。

如果互斥鎖型別為 PTHREAD_MUTEX_NORMAL則不提供死鎖檢測。嘗試重新鎖定互斥鎖會導致死鎖。如果某個執行緒嘗試解除鎖定的互斥鎖不是由該執行緒鎖定或未鎖定,則將產生不確定的行為。

如果互斥鎖型別為 PTHREAD_MUTEX_ERRORCHECK則會提供錯誤檢查。如果某個執行緒嘗試重新鎖定的互斥鎖已經由該執行緒鎖定,則將返回錯誤。如果某個執行緒嘗試解除鎖定的互斥鎖不是由該執行緒鎖定或者未鎖定,則將返回錯誤。

如果互斥鎖型別為 PTHREAD_MUTEX_RECURSIVE則該互斥鎖會保留鎖定計數這一概念。執行緒首次成功獲取互斥鎖時,鎖定計數會設定為 1。執行緒每重新鎖定該互斥鎖一次,鎖定計數就增加 1。執行緒每解除鎖定該互斥鎖一次,鎖定計數就減小 1。鎖定計數達到 0 時,該互斥鎖即可供其他執行緒獲取。如果某個執行緒嘗試解除鎖定的互斥鎖不是由該執行緒鎖定或者未鎖定,則將返回錯誤。

如果互斥鎖型別是 PTHREAD_MUTEX_DEFAULT則嘗試以遞迴方式鎖定該互斥鎖將產生不確定的行為。對於不是由呼叫執行緒鎖定的互斥鎖,如果嘗試解除對它的鎖定,則會產生不確定的行為。如果嘗試解除鎖定尚未鎖定的互斥鎖,則會產生不確定的行為。

返回值:

pthread_mutex_lock() 在成功完成之後會返回零。其他任何返回值都表示出現了錯誤。如果出現以下任一情況,該函式將失敗並返回對應的值。

EAGAIN:由於已超出了互斥鎖遞迴鎖定的最大次數,因此無法獲取該互斥鎖。

EDEADLK:當前執行緒已經擁有互斥鎖。

3)解除鎖定互斥鎖

函式原型:

int pthread_mutex_unlock(pthread_mutex_t *mutex); #include pthread_mutex_t mutex; int ret; ret = pthread_mutex_unlock(&mutex); /* release the mutex */

函式說明:pthread_mutex_unlock() 可釋放 mutex 引用的互斥鎖物件。互斥鎖的釋放方式取決於互斥鎖的型別屬性。如果呼叫 pthread_mutex_unlock() 時有多個執行緒被 mutex 物件阻塞,則互斥鎖變為可用時排程策略可確定獲取該互斥鎖的執行緒。對於 PTHREAD_MUTEX_RECURSIVE 型別的互斥鎖,當計數達到零並且呼叫執行緒不再對該互斥鎖進行任何鎖定時,該互斥鎖將變為可用。

返回值:pthread_mutex_unlock() 在成功完成之後會返回零。

其他任何返回值都表示出現了錯誤。如果出現以下情況,該函式將失敗並返回對應的值。

EPERM :當前執行緒不擁有互斥鎖。

4)使用非阻塞互斥鎖鎖定

函式原型:

int pthread_mutex_trylock(pthread_mutex_t *mutex); #include pthread_mutex_t mutex; int ret; ret = pthread_mutex_trylock(&mutex); /* try to lock the mutex */

函式說明:pthread_mutex_trylock()  pthread_mutex_lock() 的非阻塞版本。如果 mutex 所引用的互斥物件當前被任何執行緒(包括當前執行緒)鎖定,則將立即返回該呼叫。否則,該互斥鎖將處於鎖定狀態,呼叫執行緒是其屬主。

返回值:pthread_mutex_trylock() 在成功完成之後會返回零。其他任何返回值都表示出現了錯誤。如果出現以下任一情況,該函式將失敗並返回對應的值。

EBUSY :

由於 mutex 所指向的互斥鎖已鎖定,因此無法獲取該互斥鎖。

EAGAIN:描述:

由於已超出了 mutex 的遞迴鎖定最大次數,因此無法獲取該互斥鎖。

5)銷燬互斥鎖

函式原型:

int pthread_mutex_destroy(pthread_mutex_t *mp); #include pthread_mutex_t mp; int ret; ret = pthread_mutex_destroy(&mp); /* mutex is destroyed */請注意,沒有釋放用來儲存互斥鎖的空間。

返回值:

pthread_mutex_destroy() 在成功完成之後會返回零。其他任何返回值都表示出現了錯誤。如果出現以下任一情況,該函式將失敗並返回對應的值。

EINVAL: mp 指定的值不會引用已初始化的互斥鎖物件。

3.例子:

互斥鎖用來保證一段時間內只有一個執行緒在執行一段程式碼。必要性顯而易見:假設各個執行緒向同一個檔案順序寫入資料,最後得到的結果一定是災難性的。

我們先看下面一段程式碼。這是一個讀/寫程式,它們公用一個緩衝區,並且我們假定一個緩衝區只能儲存一條資訊。即緩衝區只有兩個狀態:有資訊或沒有資訊。

void reader_function ( void );

void writer_function ( void );

char buffer;

int buffer_has_item=0;

pthread_mutex_t mutex;

struct timespec delay;

void main ( void ){

 pthread_t reader;

 /* 定義延遲時間*/

 delay.tv_sec = 2;

 delay.tv_nec = 0;

 /* 用預設屬性初始化一個互斥鎖物件*/

 pthread_mutex_init (&mutex,NULL);

pthread_create(&reader, pthread_attr_default, (void *)&reader_function), NULL);

 writer_function( );

}

void writer_function (void){

 while(1){

  /* 鎖定互斥鎖*/

  pthread_mutex_lock (&mutex);

  if (buffer_has_item==0){

   buffer=make_new_item( );

   buffer_has_item=1;

  }

  /* 開啟互斥鎖*/

  pthread_mutex_unlock(&mutex);

  pthread_delay_np(&delay);

 }

}

void reader_function(void){

 while(1){

  pthread_mutex_lock(&mutex);

  if(buffer_has_item==1){

   consume_item(buffer);

   buffer_has_item=0;

  }

  pthread_mutex_unlock(&mutex);

  pthread_delay_np(&delay);

 }

}

程式說明:

這裡聲明瞭互斥鎖變數mutex,結構pthread_mutex_t為不公開的資料型別,其中包含一個系統分配的屬性物件。函式pthread_mutex_init用來生成一個互斥鎖。NULL引數表明使用預設屬性。如果需要宣告特定屬性的互斥鎖,須呼叫函式pthread_mutexattr_init。函式pthread_mutexattr_setpshared和函式pthread_mutexattr_settype用來設定互斥鎖屬性。前一個函式設定屬性pshared,它有兩個取值,PTHREAD_PROCESS_PRIVATEPTHREAD_PROCESS_SHARED。前者用來不同程序中的執行緒同步,後者用於同步本程序的不同執行緒。

在上面的例子中,我們使用的是預設屬性PTHREAD_PROCESS_ PRIVATE。後者用來設定互斥鎖型別,可選的型別有PTHREAD_MUTEX_NORMALPTHREAD_MUTEX_ERRORCHECKPTHREAD_MUTEX_RECURSIVEPTHREAD _MUTEX_DEFAULT。它們分別定義了不同的上鎖、解鎖機制,一般情況下,選用最後一個預設屬性。

pthread_mutex_lock宣告開始用互斥鎖上鎖,此後的程式碼直至呼叫pthread_mutex_unlock為止,均被上鎖,即同一時間只能被一個執行緒呼叫執行。當一個執行緒執行到pthread_mutex_lock處時,如果該鎖此時被另一個執行緒使用,那此執行緒被阻塞,即程式將等待到另一個執行緒釋放此互斥鎖。在上面的例子中,我們使用了pthread_delay_np函式,讓執行緒睡眠一段時間,就是為了防止一個執行緒始終佔據此函式。

4.飢餓和死鎖的情形

當一個互斥量已經被別的執行緒鎖定後,另一個執行緒呼叫pthread_mutex_lock()函式去鎖定它時,會掛起自己的執行緒等待這個互斥量被解鎖。可能出現以下兩種情況:

飢餓狀態”:這個互斥量一直沒有被解鎖,等待鎖定它的執行緒將一直被掛著,即它請求某個資源,但永遠得不到它。使用者必須在程式中努力避免這種“飢餓”狀態出現。Pthread函式庫不會自動處理這種情況。

死鎖”:一組執行緒中的所有執行緒都在等待被同組中另外一些執行緒佔用的資源,這時,所有執行緒都因等待互斥量而被掛起,它們中的任何一個都不可能恢復執行,程式無法繼續執行下去。這時就產生了死鎖。Pthread函式庫可以跟蹤這種情形,最後一個執行緒試圖呼叫pthread_mutex_lock()時會失敗,並返回型別為EDEADLK的錯誤。

轉載自:http://blog.chinaunix.net/uid-21411227-id-1826888.html

相關推薦

執行程式設計-互斥

1.引言: 互斥鎖,是一種訊號量,常用來防止兩個程序或執行緒在同一時刻訪問相同的共享資源。可以保證以下三點: 原子性:把一個互斥量鎖定為一個原子操作,這意味著作業系統(或pthread函式庫)保證瞭如果一個執行緒鎖定了一個互斥量,沒有其他執行緒在同一時間可以成功鎖定這個互斥

Linux下執行程式設計互斥和條件變數的簡單使用

Linux下的多執行緒遵循POSIX執行緒介面,稱為pthread。編寫Linux下的多執行緒程式,需要使用標頭檔案pthread.h,連結時需要使用庫libpthread.a。執行緒是程序的一個實體,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位。執行緒

java 執行 ReentrantLock互斥

  互斥鎖: 在上節中我們談到了Object類的notify()方法隨機喚醒單個執行緒,而不是喚醒指定執行緒,這就會導致一個問題,比如三個執行緒A,B,C,A在執行執行緒體,B,C在等待,A執行完該B執行了,notify方法隨機喚醒一個執行緒,顯然不能用,notifyAll方法把兩

python執行互斥使用

def RunThread(target, *args): #傳入一個函式多執行緒執行 print u"啟動執行緒:", target.__name__ t = threading.Thread(target = target, args = args) #t.set

執行程式設計——互斥

#include <pthread.h> #include <unistd.h> #include <stdio.h> /* 執行緒控制塊 */ static pthread_t tid1; static pthread_t tid2; /* 函式返回

執行互斥(By C++)與程序

#include<Windows.h> #include<iostream> using namespace std; //互斥鎖 HANDLE hMutex1; HANDLE hMutex2; int flag; DWORD WINAPI MyThread2(LPVOID lp

Linux下c++執行互斥

一、多執行緒 多執行緒使用的是pthread庫,寫程式的時候需要引入標頭檔案pthread.h, g++編譯的時候需要加上該庫的依賴“-lpthread”。 1 先看程式便於理解,程式碼下面有對註釋的解釋。下面的程式碼含義是建立兩個執行緒,一個執行緒去計算某

執行互斥和條件變數實現生產者和消費者-------迴圈任務佇列

互斥鎖與條件變數簡介 在多執行緒的環境中,全域性變數會被各執行緒共享,因此在操作全域性變數的時候需要採用鎖機制,在linux裡最常用的鎖就是互斥鎖,互斥鎖使用方法如下 <pre name="code" class="cpp">//執行緒A pthread_mut

執行互斥的問題

最近在多執行緒程式設計中遇到了這樣一個情況,程式中有一些變數是全域性有效的,多個執行緒都要訪問,由於沒有考慮太多,導致執行緒出現一些問題。於是乎,就想到了互斥鎖,可是遇到了更嚴重的情況:有些執行緒執行一段時間後會被其父執行緒殺掉,假若此時它已對互斥鎖執行了加鎖操作而又未解鎖的

Java執行程式設計執行的同步與互斥/執行安全/Java

摘要:多執行緒三個特徵:原子性、可見性以及有序性.&gt;執行緒的同步與互斥?(同步執行緒與非同步執行緒,執行緒同步和非同步問題)&nbsp;&nbsp;1.同步:假設現有執行緒A和執行緒B,執行緒A需要往緩衝區寫資料,執行緒B需要從緩衝區讀資料,但他們之間存在一種制約

Linux執行程式設計---執行間同步(互斥、條件變數、訊號量和讀寫

本篇博文轉自http://zhangxiaoya.github.io/2015/05/15/multi-thread-of-c-program-language-on-linux/ Linux下提供了多種方式來處理執行緒同步,最常用的是互斥鎖、條件變數、訊號量和讀寫鎖。  下面是思維導

【Linux C 執行程式設計互斥與條件變數

一、互斥鎖互斥量從本質上說就是一把鎖, 提供對共享資源的保護訪問。  1. 初始化:  在Linux下, 執行緒的互斥量資料型別是pthread_mutex_t. 在使用前, 要對它進行初始化:  對於靜態分配的互斥量, 可以把它設定為PTHREAD_MUTEX_INITIA

C/C++_執行程式設計_互斥

前言 定義/概念 問題的出現 在現代作業系統中,出現不止一個物件(程序/執行緒)對程式碼進行訪問是一個普遍的現象(併發的來源),這也就會出現對共享的資源(記憶體、硬體資源等)出現競爭佔有的狀態(競態)。資源是必須用於多個物件共享的,但是我們又不

Linux程式設計學習筆記----執行程式設計執行同步機制之互斥量()與讀寫

互斥鎖通訊機制 基本原理 互斥鎖以排他方式防止共享資料被併發訪問,互斥鎖是一個二元變數,狀態為開(0)和關(1),將某個共享資源與某個互斥鎖邏輯上繫結之後,對該資源的訪問操作如下: (1)在訪問該資源之前需要首先申請互斥鎖,如果鎖處於開狀態,則申請得到鎖並立即上鎖(關),防

python執行程式設計(3): 使用互斥同步執行

問題的提出 上一節的例子中,每個執行緒互相獨立,相互之間沒有任何關係。現在假設這樣一個例子:有一個全域性的計數num,每個執行緒獲取這個全域性的計數,根據num進行一些處理,然後將num加1。很容易寫出這樣的程式碼: # encoding: UTF-8import

linux執行程式設計互斥

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <pthread.h>

執行程式設計總結(二)——條件變數和互斥

#include <stdio.h> #include <pthread.h> #include <error.h> #include <assert.h> #include <stdlib.h> typedef int DataType; typ

Java執行程式設計優化

作者:melonstreet 連結:www.cnblogs.com/QG-whz 閱讀目錄 一、儘量不用:儘量不要鎖住方法 二、減小粒度:縮小同步程式碼塊,只鎖資料 三、避免巢狀:鎖中儘量不要再包含鎖 四、鎖私有化:將鎖私有化,在內部管理鎖 五、適當分解:進行適當的鎖分解

Python中的執行程式設計執行安全與(一) 聊聊Python中的GIL 聊聊Python中的GIL python基礎之執行機制 python--threading執行總結 Python3入門之執行threading常用方法

1. 多執行緒程式設計與執行緒安全相關重要概念 在我的上篇博文 聊聊Python中的GIL 中,我們熟悉了幾個特別重要的概念:GIL,執行緒,程序, 執行緒安全,原子操作。 以下是簡單回顧,詳細介紹請直接看聊聊Python中的GIL  GIL:&n

Python執行程式設計,執行

1 2 3 from threading import Thread 4 import time 5 ​ 6 class MyThread(Thread): 7 name1 = 'MyThread-1' 8 def __init__(self,target,args