Linux--線程死鎖

http://blog.csdn.net/gebushuaidanhenhuai/article/details/73799824

線程為什會死鎖??“鎖”又是什么東西?我們這篇博客主要講一下為什么要給線程加鎖,為什么會出現線程死鎖,線程死鎖怎么解決。

互斥鎖

在我的上篇博客已經講解了一些線程的基本知識Linux–線程控制我們可以了解到線程是共享同一份內存的。這就意味著多個線程同時訪問共享數據時可能發生沖突。

分析程序


我們首先分析一個小程序:

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>static int g_count = 0;
void* read_write_num(void* _val)
{int val = 0;int i = 0;for(; i<5000; ++i){val  = g_count;printf("pthread id is:%x,count is : %d\n",(unsigned long)pthread_self(),g_count);g_count = val + 1;}return NULL;
}int main()
{pthread_t tid1;pthread_t tid2;pthread_create(&tid1,NULL,read_write_num,NULL);pthread_create(&tid2,NULL,read_write_num,NULL);pthread_join(tid1,NULL);pthread_join(tid2,NULL);printf("count final val is:%d\n ",g_count);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

這里寫圖片描述

上面程序是創建兩個線程,各自都把全局靜態變量count增加5000次。正常情況下結果應該是10000,但是程序的最終結果總是5000左右,與正確結果相差很遠,這是為什么呢??
這是因為對多線程的程序,發生了訪問沖突了。對一個線程要對一個變量進行加1操作,需要進行三步:?
1.將數據從內存單元讀入寄存器。?
2.把寄存器中的變量做加1操作。?
3.把加1后的變量重新寫回內存單元。

我們來模擬一下上面程序的運行。我們上面的程序有兩個線程tid1,tid2。假如此時變量count = 10.?
當tid1進行第二步時,從內存讀到10,在寄存器加1–>10+1=11。?
但是此時tid2切入,從內存讀到10,在寄存器加1–>10+1=11。?
因為tid1還沒有把11寫入到內存中,tid2讀到的值并不是我們想要的11,這就引發了問題。?
解決的方法就是引入互斥鎖,獲得鎖線程可以完成“讀–修改–寫”的操作,然后釋放鎖給其他線程,沒有獲得鎖的線程只能等待而不能訪問共享數據。這樣“讀–修改–寫”散步操作組成了一個原子操作,要么都執行,要么都不執行。

互斥鎖操作函數

互斥鎖的數據類型為pthread_mutex_t.?
創建互斥鎖:

#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t* restrict mutex,const pthread_mutexattr_t* restrict attr)
  • 1
  • 2

返回值:成功返回0,失敗返回錯誤號。?
參數:?
pthread_mutex_init()函數是以動態方式創建互斥鎖的,參數attr指定了新建互斥鎖的屬性。如果參數attr為空,則使用默認的互斥鎖屬性,默認屬性為快速互斥鎖 。?
如果Mutex變量是靜態分配的,也可以利用宏定義來初始化。

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  • 1

此方法相當于pthread_mutex_init初始化并且attr參數為NULL.?
銷毀互斥鎖:

#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
  • 1
  • 2

返回值:成功返回0,失敗返回錯誤碼。?
加鎖:

#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
  • 1
  • 2

解鎖:

#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 1
  • 2

返回值:成功返回0,失敗返回錯誤號。?
一個線程可以調用pthread_mutex_lock還會獲得Mutex,若果這時另一個線程已經調用pthread_mutex_lock獲得了該Mutex,則當前進程需要掛起等待,直到另一個線程調用pthread_mutex_unlock釋Mutex,當前進程被喚醒,才能獲得該Mutex并繼續執行。?
如果一個線程既想獲得鎖,又不想掛起等待,可以調用pthread_mutex_trylock,如果Mutex已經被另一個線程獲得,這個函數會失敗返回EBUSY,而不會使線程掛起等待。

#include <pthread.h>
int pthread_mutex_trylock(pthread_mutex_t *mutex);
  • 1
  • 2

這時我們就可以解決上面那個代碼的bug了。

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>static int g_count = 0;
pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER;
void* read_write_num(void* _val)
{int val = 0;int i = 0;for(; i<5000; ++i){pthread_mutex_lock(&mutex_lock);val  = g_count;printf("pthread id is:%x,count is : %d\n",(unsigned long)pthread_self(),g_count);g_count = val + 1;pthread_mutex_unlock(&mutex_lock);}return NULL;
}int main()
{pthread_t tid1;pthread_t tid2;pthread_create(&tid1,NULL,read_write_num,NULL);pthread_create(&tid2,NULL,read_write_num,NULL);pthread_join(tid1,NULL);pthread_join(tid2,NULL);printf("count final val is:%d\n ",g_count);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

這里寫圖片描述

線程死鎖

?般情況下,如果同?個線程先后兩次調?lock,在第?次調?時,由于鎖已經被占?,該線程會掛起等待別的線程釋放鎖,然?鎖正是被??占?著的,該線程又被掛起?沒有機會釋放鎖,因此 就永遠處于掛起等待狀態了,這叫做死鎖(Deadlock)。?
另一種情況:線程A獲 得了鎖1,線程B獲得了鎖2,這時線程A調?lock試圖獲得鎖2,結果是需要掛起等待線程B釋放 鎖2,?這時線程B也調?lock試圖獲得鎖1,結果是需要掛起等待線程A釋放鎖1,于是線程A和B都 永遠處于掛起狀態了。?
死鎖產生的4個必要條件:?
1)互斥條件:指進程對所分配到的資源進行排它性使用,即在一段時間內某資源只由一個進程占用。如果此時還有其它進程請求資源,則請求者只能等待,直至占有資源的進程用畢釋放。?
2)請求和保持條件:指進程已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它進程占有,此時請求進程阻塞,但又對自己已獲得的其它資源保持不放。?
3)不剝奪條件:指進程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時由自己釋放。?
4)環路等待條件:指在發生死鎖時,必然存在一個進程——資源的環形鏈,即進程集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1占用的資源;P1正在等待P2占用的資源,……,Pn正在等待已被P0占用的資源。?
預防死鎖:?
只要打破四個必要條件之一就能有效預防死鎖的發生:打破互斥條件:改造獨占性資源為虛擬資源,大部分資源已無法改造。?
打破不可搶占條件:當一進程占有一獨占性資源后又申請一獨占性資源而無法滿足,則退出原占有的資源。?
打破占有且申請條件:采用資源預先分配策略,即進程運行前申請全部資源,滿足則運行,不然就等待,這樣就不會占有且申請。?
打破循環等待條件:實現資源有序分配策略,對所有設備實現分類編號,所有進程只能采用按序號遞增的形式申請資源。

有序資源分配法


這種算 法資源按某種規則系統中的所有資源統一編號(例如打印機為1、磁帶機為2、磁盤為3、等等),申請時必須以上升的次序。系統要求申請進程:?
1、對它所必須使用的而且屬于同一類的所有資源,必須一次申請完;?
2、在申請不同類資源時,必須按各類設備的編號依次申請。例如:進程PA,使用資源的順序是R1,R2; 進程PB,使用資源的順序是R2,R1;若采用動態分配有可能形成環路條件,造成死鎖。?
采用有序資源分配法:R1的編號為1,R2的編號為2;?
PA:申請次序應是:R1,R2?
PB:申請次序應是:R1,R2?
這樣就破壞了環路條件,避免了死鎖的發生

銀行家算法


避免死鎖算 法中最有代表性的算 法是Dijkstra E.W 于1968年提出的銀行家 算 法:?
銀行家算法是避免死鎖的一種重要方法,防止死鎖的機構只能確保上述四個條件之一不出現,則系統就不會發生死鎖。通過這個算法可以用來解決生活中的實際問題,如銀行貸款等。?
程序實現思路銀行家算法顧名思義是來源于銀行的借貸業務,一定數量的本金要應多個客戶的借貸周轉,為了防止銀行家資金無法周轉而倒閉,對每一筆貸款,必須考察其是否能限期歸還。在操作 系統中研究資源分配策略時也有類似問題,系統中有限的資源要供多個進程使用,必須保證得到的資源的進程能在有限的時間內歸還資源,以供其他進程使用資源。如果資源分配不得到就會發生進程循環等待資源,則進程都無法繼續執行下去的死鎖現象。?
把一個進程需要和已占有資源的情況記錄在進程控制中,假定進程控制塊PCB其中“狀態”有就緒態、等待態和完成態。當進程在處于等待態時,表示系統不能滿足該進程當前的資源申請。“資源需求總量”表示進程在整個執行過程中總共要申請的資源量。顯然,每個進程的資源需求總量不能超過系統擁有的資源總數, 銀行算法進行資源分配可以避免死鎖。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/384061.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/384061.shtml
英文地址,請注明出處:http://en.pswp.cn/news/384061.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

兩個隊列實現一個棧

用兩個隊列實現一個棧的原理是這樣的. 規定兩個隊列, 必須有一個隊列是非空, 一個隊列是空.每次入棧時必須往非空隊列中入, 而每次出棧時, 必須將非空隊列里的元素裝到空隊列中, 直到非空隊列中只有一個元素時, 此時就將剩下的這個元素出棧即可. 而取棧頂元素時, 和出棧一樣, 先…

POJ-1144 Network——Trajan+割點

【題目描述】 A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting several places numbered by integers from 1 to N . No two places have the same number. The lines are bidirectional and always connect together tw…

Linux--生產者與消費者

http://blog.csdn.net/gebushuaidanhenhuai/article/details/74011636 基本概念 提到生產者和消費者&#xff0c;我們最有可能想到的是商店賣東西&#xff0c;顧客在貨架上(緩沖區&#xff09;買東西。 生產者消費者問題&#xff0c;其實是一個多線程同步問題的經典案例。該問…

進程的掛起以及可重入函數

相關接口 ????pause 函數用于將進程掛起. 如果信號的處理動作是終止進程, 則進程終止, pause 函數沒有返回值; 如果信號的處理動作是忽略, 則進程被掛起, pause函數不返回, 如果信號的處理動作是捕捉, 則調用信號處理動作之后pause 返回 -1.來看一段代碼 #include<s…

POJ1236Network of Schools——強連通分量縮點建圖

【題目描述】 A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the “receiving schools”). Note that if B is in the distri…

C——通過調用函數分配內存

http://blog.csdn.net/u012627502/article/details/3579724 1&#xff09;以返回值方式返回&#xff1a;把動態分配的存儲位置地址&#xff0c;賦值給指針類型返回值&#xff08;不同于被調用函數的自動變量地址&#xff09; 2&#xff09;以形參形式返回&#xff1a;二級指針類…

gdb調試多進程程序

1.gdb下調試多進程程序只需要以下幾條命令即可 ???????? ????除此之外還可以查看正在調試的進程 info inferiors, 同時也可以將當前正在調試的進程切換到另外一個進程中讓其取運行 ????2.代碼調試演示 #include<stdio.h> #include<stdlib.h> #…

BZOJ1123-BLO——強連通分量求割點+計數

【題目描述】 Byteotia城市有n個 towns m條雙向roads. 每條 road 連接 兩個不同的 towns ,沒有重復的road. 所有towns連通。Input 輸入n<100000 m<500000及m條邊Output 輸出n個數&#xff0c;代表如果把第i個點去掉&#xff0c;將有多少對點不能互通。Sample Input 5…

關于memcpy和memmove兩函數的區別

http://blog.csdn.net/caowei840701/article/details/8491836 [cpp] view plaincopy <p> 關于memcpy和memmove兩個c標準庫函數&#xff0c;其功能都是將一塊內存區域中的指定大小內容復制到目標內存中&#xff0c;在翻閱c標準庫實現的源代碼我們發現他們是有區別的。&…

判斷字符串出棧合法性

先來看說一下思路 接下來就是寫代碼了 int StackOrder(SeqStack* stack, char* input, char* output, int size_input, int size_output) {if(stack NULL || input NULL || output NULL){return 0;}int i_input 0;int j_output 0;SeqStackType value;for(; j_output <…

CodeForces - 1200C——小模擬

【題目描述】 Amugae is in a very large round corridor. The corridor consists of two areas. The inner area is equally divided by n sectors, and the outer area is equally divided by m sectors. A wall exists between each pair of sectors of same area (inner o…

1 單例模式

達內 閔大神 //餓漢單例模式 #include <iostream> using namespace std;class Singleton { public:static Singleton& getInstance(){return s_instance;} private:Singleton(){}Singleton(const Singleton& that){}static Singleton s_instance;//靜態成員變量 …

共享棧

1.定義 所謂共享棧就是利用一個數組實現兩個棧. 先來看一下共享棧的數據結構 typedef struct SharedStack {int top1;int top2;SeqStackType* data; }SharedStack; 2. 初始化 void SharedStackInit(SharedStack* stack) {if(stack NULL){return;//非法輸入}stack -> top…

BZOJ1018 | SHOI2008-堵塞的交通traffic——線段樹維護區間連通性+細節

【題目描述】 BZOJ1018 | SHOI2008-堵塞的交通traffic 有一天&#xff0c;由于某種穿越現象作用&#xff0c;你來到了傳說中的小人國。小人國的布局非常奇特&#xff0c;整個國家的交通系統可 以被看成是一個2行C列的矩形網格&#xff0c;網格上的每個點代表一個城市&#xff0…

C++ 函數隱藏

C該函數隱藏 只有基類成員函數的定義已聲明virtualkeyword&#xff0c;當在派生類中的時間&#xff0c;以支付功能實現&#xff0c;virtualkeyword可以從時間被添加以增加。它不影響多狀態。 easy混淆視聽&#xff0c;掩蓋&#xff1a; &#xff0c;規則例如以下&#xff1a; …

迷宮求解(遞歸)

首先來看一下迷宮簡易圖 ???????????????????????????? ????我們用 0 來表示該位置是墻, 用 1 來表示該位置是路. 所以, 我們在處理迷宮問題的時候可以將其看成一個二維數組即可, 而對應的每一條路我們可以用坐標的形式將其表示, 所以還需要…

CodeForces - 786C——二分+模擬?

【題目描述】 Rick and Morty want to find MR. PBH and they cant do it alone. So they need of Mr. Meeseeks. They Have generated n Mr. Meeseeks, standing in a line numbered from 1 to n. Each of them has his own color. i-th Mr. Meeseeks color is ai.Rick and M…

迷宮求解(非遞歸)

上篇文章寫出了利用函數形成棧楨的特性完成迷宮求解問題, 本篇文章我們自己手動維護一個棧, 其進行出棧, 入棧, 取棧頂元素, 來完成迷宮求解尋路的過程 ????思路和以前一樣, 首先, 我們先定義一個棧, 對其初始化, 同時, 定義一個迷宮地圖, 對該地圖進行初始化, 先判斷當前…

數據結構練習——雙向鏈表

http://www.cnblogs.com/-Lei/archive/2012/04/10/2440399.html 復習一下數據結構。。。。說不準下個星期就用上了 只不過寫的很簡單&#xff0c;沒有封裝 DoubleLinkList.h #ifndef GUARD_DoubleLinkList_h #define GUARD_DoubleLinkList_h#include <stdio.h>struct Li…

CodeChef - DGCD——樹鏈剖分+差分

【題目描述】 Youre given a tree on N vertices. Each vertex has a positive integer written on it, number on the ith vertex being vi. Your program must process two types of queries :1. Find query represented by F u v : Find out gcd of all numbers on the uniq…