C 中死鎖的產生及解決

2021-10-08 14:50:34 字數 4180 閱讀 3510

互斥條件:指程序對所分配到的資源進行排它性使用,即在一段時間內某資源只由乙個程序占用。如果此時還有其它程序請求資源,則請求者只能等待,直至占有資源的程序用畢釋放。

請求和保持條件:指程序已經保持至少乙個資源,但又提出了新的資源請求,而該資源已被其它程序占有,此時請求程序阻塞,但又對自己已獲得的其它資源保持不放。

不剝奪條件:指程序已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。

環路等待條件:指在發生死鎖時,必然存在乙個程序——資源的環形鏈,即程序集合中的p0正在等待乙個p1占用的資源;p1正在等待p2占用的資源,……,pn正在等待已被p0占用的資源。

例子:

class

some_big_obj

;void

swap

(some_big_obj& lhs, some_big_obj& rhs)

;class

xfriend

void

swap

(x& lhs, x& rhs)

private

: some_big_obj some_detail;

std::mutex m;

};

上面的1處和2處分別鎖住了兩個互斥量,假如執行緒a在執行1的時候,執行緒切換d到b,b執行緒鎖住了rhs的鎖,並開始嘗試鎖住lhs的鎖,但是發現lhs的鎖已經被鎖住了(被a鎖住了),所以b執行緒發生了阻塞。然後執行緒切換回a,開始去嘗試鎖住rhs,但是rhs此時已經被b執行緒鎖住了,此時a執行緒也發生了阻塞。兩個執行緒會分別等待對方釋放另乙個鎖,所以就無限等待了。

解決方法:

class

some_big_obj

;void

swap

(some_big_obj& lhs, some_big_obj& rhs)

;class

xfriend

void

swap

(x& lhs, x& rhs)

private

: some_big_obj some_detail;

std::mutex m;

};

有兩種方法:

直接使用c++17標準的std::scope_lock模板類,此模板類可以同時鎖定多個互斥量,並且能夠在析構時自動解鎖互斥量;

使用c++11標準的std::lock()模板函式和std::lock_guard模板類,通過函式std::lock()鎖定多個互斥量,然後通過std::lock_guard類負責互斥量的解鎖。其中,std::adopt_lock表示只構造物件,但不鎖定互斥量。

例子:

std::mutex m;

voidf(

)int

main()

上述過程可能導致在2處上鎖,然後子執行緒在1處發生阻塞,最後主線程在3處一直等待子執行緒結束,無窮等待下去。

解決方法:

主線程對互斥量上鎖要放到join()之後;

std::thread t

(f);

// ...

t.join()

;// ...

std::lock_guard lock

(m);

// ...

主線程對互斥量上鎖放到乙個區域性作用域內。

std::thread t

(f);

t.join()

;

因為使用者提供的**中很可能也有鎖,這樣的話,在同乙個過程可能就會訪問多個鎖,可能會產生第1點所描述的死鎖。

比如多執行緒下對於鍊錶的操作。假如保護鍊錶的鎖的粒度很小,每個節點擁有乙個互斥量,這樣能夠最大化並行效率。在這種情況下,當乙個執行緒刪除鍊錶中的乙個節點時,它必須獲取3個節點上的互斥量:將要刪除的節點,兩個鄰接節點。

執行緒1執行緒2

鎖住主入口的互斥量

讀取頭節點指標

鎖住頭節點互斥量

解鎖主入口互斥量

鎖住主入口互斥量

讀取head->next指標

鎖住尾結點互斥量

鎖住next節點互斥量

讀取tail->prev指標

讀取next->next指標

解鎖尾結點互斥量……

鎖住a節點互斥量

鎖住c節點互斥量

讀取a->next指標(也就是b節點)

讀取c->next指標(也就是b節點)

鎖住b節點互斥量

阻塞,嘗試鎖住b節點互斥量

解鎖c節點互斥量

讀取b->prev指標(也就是a節點)

阻塞,嘗試鎖住a節點互斥量

死鎖死鎖

由於這裡獲取三個鎖不是同時的,是分步獲取的,所以不能夠使用std::scoped_lock,所以就會產生上述的死鎖。

避免這種死鎖的方式就是定義遍歷的順序,乙個執行緒必須先鎖住a才能獲取b的鎖,在鎖住b之後才能獲取c的鎖。

在某些情況下,設定鎖的大小,使得乙個執行緒中獲取多個鎖時,只能按照順序來進行上鎖,如果順序錯了,直接丟擲異常,這樣就能避免死鎖。

層次鎖的實現例子:

class

hierarchical_mutex

void

update_hierarchy_value()

public

:explicit

hierarchical_mutex

(unsigned

long value)

:hierarchy_value

(value)

,previous_hierarchy_value(0

)void

lock()

void

unlock()

bool

try_lock()

};thread_local

unsigned

long hierarchical_mutex::

this_thread_hierarchy_value

(ulong_max)

;

其中,thread_local型別用於執行緒區域性儲存(stl)。需要注意的是,如果想讓自定義鎖型別能夠使用標準庫的std::lock()std::lock_guard,則必須自己實現類成員函式lock()unlock()try_lock()

層次鎖的使用例子:

hierarchical_mutex high_level_mutex

(10000);

hierarchical_mutex low_level_mutex

(5000);

hierarchical_mutex other_mutex

(6000);

intdo_low_level_stuf()

;int

low_level_func()

void

do_high_level_stuff

(int some_param)

;void

high_level_func()

void

thread_a()

void

do_other_stuff()

;void

other_func()

void

thread_b()

上述例子中,thread_a執行緒先得到標記為10000的鎖,然後又得到標記為5000的鎖,因此能正常執行。而thread_b先得到標記為6000的鎖,然後又嘗試獲取標記為10000的鎖,這裡就會出錯,因為獲取鎖的順序出錯了,最後會丟擲異常。

死鎖產生原因及解決方法

死鎖 執行緒級 死鎖是指兩個或兩個以上的執行緒在執行過程中,由於競爭資源而造成的一種阻塞的現象 產生的4個條件 1 互斥使用 即乙個執行緒在使用時,另外執行緒不可使用 2 不可搶占資源 資源請求者不能強制從資源佔有者手中奪取資源 3 占有且等待 當前執行緒在請求其他資源時,保持對原有資源占有 4 迴...

死鎖的產生條件及解決方法

產生死鎖的原因主要是 1 因為系統資源不足。2 程序執行推進的順序不合適。3 資源分配不當等。如果系統資源充足,程序的資源請求都能夠得到滿足,死鎖出現的可能性就很低,否則 就會因爭奪有限的資源而陷入死鎖。其次,程序執行推進順序與速度不同,也可能產生死鎖。產生死鎖的四個必要條件 1 互斥條件 乙個資源...

解決產生oracle sql死鎖

1 查詢資料庫死鎖 可以檢視產生死鎖的使用者,死鎖狀態,狀態,產生死鎖的機器和應用程式 2 查詢產生死鎖的語句 select sql text from v sql where hash value in select sql hash value from v session where sid ...