資料共享之互斥量mutex

2021-06-26 07:10:17 字數 2934 閱讀 4317

互斥量可以保護某些**只能有乙個執行緒執行這些**。如果有個執行緒使用互斥量執行某些**,其他執行緒訪問是會被阻塞,直到這個執行緒執行完這些**,其他執行緒才可以執行。

乙個執行緒在訪問共享資料前,給互斥量上鎖,這時其他執行緒再給互斥量上鎖會阻塞直到這個執行緒給互斥量解鎖。

互斥量是c++中最常用的資料保護機制,但是它也不萬能的。在編寫**時,合理的組織**來避免資源競爭非常重要。使用互斥量可能會帶來其他問題,比如死鎖。

建立互斥量使用mutex,給互斥量上鎖使用函式lock(),給互斥量解鎖使用unlock()函式。在實際應用中,不建議直接使用上鎖、解鎖函式,因為你必須記得每次上鎖後要解鎖,否則會造成死鎖。在標準庫中提供了lock_guard類模板,它使用了raii(資源申請即初始化),在建構函式中上鎖,在析構函式中解鎖。

下面乙個例子,給鍊錶新增結點和查詢鍊錶中是否包含某個元素,在操作鍊錶前都需要包含鍊錶。

#include//包含互斥量標頭檔案

#include#include#include#include#include//兩個全域性變數,用於執行緒間共享

std::listsome_list;

std::mutex some_mutex;

void add_to_list(int new_value)

bool list_cotains(int value_to_find)

void fun1()

}void fun2()

}int main()

保護共享資料,不是簡單的在每個函式裡加上lock_guard()物件。指標或引用使用不當回破壞保護的資料。查詢迷途指標或引用比較容易,只要在成員函式裡不返回共享資料的指標或引用就可以。如果在進一步看,沒那麼簡單。也可能會向你不能控制的函式傳遞共享資料的指標或引用,這些函式可能會把指標或引用儲存起來,後面在用。這樣的話,互斥量就沒法保護共享資料了。

#include//包含互斥量標頭檔案

#include#includeclass some_data

void do_something() };

};some_data *unprotected;

//把模板函式定義為引用傳遞

void maliciout_function(some_data& protected_data)

int main()

在保護共享資料的作用域內,不要返回共享資料的指標、引用,也不要把共享資料作為引數傳遞給其他人提供的函式。

一些介面,對於單執行緒來說是沒有資源競爭的,但是多個執行緒使用時就未必了。例如:

std::stacks;

void func()

}

在多執行緒環境下,先判斷棧s大小,再處理。如果再處理過程中,其他執行緒給棧新增或刪除元素,上面**就會有問題。

單個介面是安全的,但是介面組合使用就未必了。假設棧的介面函式收保護,在某一時刻只能有乙個執行。那麼下面**:

if (!s.empty())// 執行緒1				

if (!s.empty())// 執行緒2

int const value = s.top(); int const value = s.top();

s.pop(); s.pop();

do_something(value); do_something(value);

這是執行緒1和執行緒2將去到相同的資料,但是兩個執行緒都pop(),將會造成乙個資料未被使用。解決這個問題的乙個辦法是把top()和pop()用互斥量保護起來。但是tom cargill指出,如果物件的複製建構函式在棧上且丟擲異常,上面做法就會有問題。

假設把top和pop做成了原子操作,如果在top時,開闢空間失敗,這是物件不能返回,而後面又執行了pop,這時元素在沒有使用情況下被清除了。所以把top和pop做成兩個介面還是有道理的。

有下面集中解決辦法:

1、在pop時傳遞乙個引用引數

//void pop(int &value);在刪除時,把值賦給value

int k;

s.pop(k)

2、使用無異常的複製建構函式

在c++11中,右值引用使得move construtor不丟擲異常(即使複製建構函式丟擲異常)。乙個有效的辦法是:限制棧中元素的型別,僅使用不丟擲異常的資料型別。

這樣做是安全的,但是不是理想的。即使在編譯階段就可以借助std::is_nothrow_copy_constructible和std::is_nothrow_move_constructible型別來檢測複製建構函式或move constructor是否會丟擲異常,但是許多使用者自定義型別有複製建構函式,卻沒有move constructor。這樣的話,這種型別就不能儲存到執行緒安全的棧。

3、返回pop物件的指標

返回指標而不是返回值,因為指標可以安全的複製,不會有異常。缺點是返回指標要開闢記憶體、保證記憶體不會洩露。不過可以借助shared_ptr來解決。

4、使用1和2或者1和3的組合。

下面是乙個執行緒安全的棧

#include#include#include#includestruct empty_stack :std::exception

;templateclass threadsafe_stack

threadsafe_stack(const threadsafe_stack& other)

//不允許使用=

threadsafe_stack& operator=(const threadsafe_stack& ) = delete;

void push(t new_value)

std::shared_ptrpop()

void pop(t& value)

bool empty()const

};

執行緒同步之互斥量Mutex

前面的文章介紹了執行緒的建立 終止 連線和分離。本篇介紹執行緒的同步。多執行緒的難點是對共享資源的訪問,如何保證多個執行緒能夠 同時 訪問同乙個共享資源而又不引起衝突,就是執行緒同步的本質。互斥量用來確保共享資源同時只被乙個執行緒訪問。互斥量有兩種狀態 已鎖定 locked 和未鎖定 unlocke...

Linux執行緒同步之互斥量(mutex)

互斥量 也稱為互斥鎖 出自posix 執行緒標準,可以用來同步同一程序中的各個執行緒。當然如果乙個互斥量存放在多個程序共享的某個記憶體區中,那麼還可以通過互斥量來進行程序間的同步。互斥量,從字面上就可以知道是相互排斥的意思,它是最基本的同步工具,用於保護臨界區 共享資源 以保證在任何時刻只有乙個執行...

Linux之執行緒 互斥量mutex與死鎖

主要內容 1 互斥量及如何使用 2 什麼是死鎖,如何解決 3 什麼是讀寫鎖,如何使用 4 條件變數實現的生產消費者模型 5 訊號量實現的生產消費者模型 執行緒同步 協調步驟,順序執行。解決同步的問題 加鎖!資料混亂的原因 資源共享 獨享資源則不會 隨機排程 意味著資料訪問會出現競爭 執行緒間缺乏必要...