多線程死鎖

轉(zhuǎn)載自 [http://www.cocoachina.com/ios/20161129/18216.html]
死鎖產(chǎn)生的條件:

互斥條件:一個資源每次只能被一個進程使用。
請求與保持條件:一個進程因請求資源而阻塞時谨垃,對已獲得的資源保持不放。
不剝奪條件:進程已獲得的資源窖式,在末使用完之前,不能強行剝奪动壤。 
循環(huán)等待條件:若干進程之間形成一種頭尾相接的循環(huán)等待資源關系萝喘。

解決死鎖的四個方式.

忽略該問題。例如鴕鳥算法,該算法可以應用在極少發(fā)生死鎖的的情況下阁簸。為什么叫鴕鳥算法呢爬早,(鴕鳥策略) 
檢測死鎖并且恢復。(檢測與解除策略) 
仔細地對資源進行動態(tài)分配启妹,以避免死鎖筛严。(避免策略) 
通過破除死鎖四個必要條件之一,來防止死鎖產(chǎn)生饶米。(預防策略)

進程(Process)和線程(Thread的區(qū)別
進程是具有一定獨立功能的程序關于某個數(shù)據(jù)集合上的一次運行活動桨啃,進程是系統(tǒng)進行資源分配和調(diào)度的一個獨立單位。擁有獨立的內(nèi)存單元檬输。線程是進程的一個實體照瘾,是CPU調(diào)度和分派的基本單位,它是比進程更小的能獨立運行的基本單位丧慈。但是不能獨立運行析命,必須依存在應用程序中,由應用程序提供多個線程執(zhí)行控制伊滋。線程自己基本上不擁有系統(tǒng)資源碳却,只擁有一點在運行中必不可少的資源(如程序計數(shù)器,一組寄存器和棧)笑旺,但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源。一個線程可以創(chuàng)建和撤銷另一個線程馍资,同一個進程中的多個線程之間可以并發(fā)執(zhí)行筒主。
  進程與應用程序的區(qū)別在于應用程序作為一個靜態(tài)文件存儲在計算機系統(tǒng)的硬盤等存儲空間中,而進程則是處于動態(tài)條件下由操作系統(tǒng)維護的系統(tǒng)資源管理實體鸟蟹。

進程的狀態(tài)轉(zhuǎn)換圖乌妙,及導致轉(zhuǎn)換的事件.
三個狀態(tài):

1)就緒狀態(tài)  進程已獲得除處理機外的所需資源,等待分配處理機資源建钥,只要分配到CPU就可執(zhí)行藤韵。在某一時刻,可能有若干個進程處于該狀態(tài)熊经。    
2)運行狀態(tài) 占用處理機資源運行泽艘,處于此狀態(tài)的進程的數(shù)目小于等于CPU的數(shù)目。   
3)阻塞狀態(tài)  由于進程等待某種條件(如I/O操作或進程同步)镐依,在條件滿足之前無法繼續(xù)執(zhí)行匹涮。該事件發(fā)生前即使把處理機分配給該進程,也無法運行槐壳。
線程狀態(tài)

鎖的分類

互斥鎖(mutexlock):
最常使用于線程同步的鎖然低;標記用來保證在任一時刻,只能有一個線程訪問該對象,同一線程多次加鎖操作會造成死鎖雳攘;臨界區(qū)和互斥量都可用來實現(xiàn)此鎖带兜,通常情況下鎖操作失敗會將該線程睡眠等待鎖釋放時被喚醒
NSLock
NSLock實現(xiàn)了最基本的互斥鎖,遵循了 NSLocking 協(xié)議吨灭,通過 lock 和 unlock 來進行鎖定和解鎖刚照。其使用也非常簡單

- (void)doSomething {
   [self.lock lock];
   //TODO: do your stuff
   [self.lock unlock];
}

NSConditionLock
NSConditionLock 對象所定義的互斥鎖可以在使得在某個條件下進行鎖定和解鎖。它和 NSCondition 很像沃于,但實現(xiàn)方式是不同的涩咖。
當兩個線程需要特定順序執(zhí)行的時候,例如生產(chǎn)者消費者模型繁莹,則可以使用 NSConditionLock 檩互。當生產(chǎn)者執(zhí)行執(zhí)行的時候,消費者可以通過特定的條件獲得鎖咨演,當生產(chǎn)者完成執(zhí)行的時候闸昨,它將解鎖該鎖,然后把鎖的條件設置成喚醒消費者線程的條件薄风。鎖定和解鎖的調(diào)用可以隨意組合饵较,lock 和 unlockWithCondition: 配合使用 lockWhenCondition: 和 unlock 配合使用。

- (void)producer {
    while (YES) {
        [self.conditionLock lock];
        NSLog(@"have something");
        self.count++;
        [self.conditionLock unlockWithCondition:1];
    }
}
- (void)consumer {
    while (YES) {
        [self.conditionLock lockWhenCondition:1];
        NSLog(@"use something");
        self.count--;
        [self.conditionLock unlockWithCondition:0];
    }
}

pthread_mutex
POSIX 互斥鎖是一種超級易用的互斥鎖遭赂,使用的時候循诉,只需要初始化一個 pthread_mutex_t 用 pthread_mutex_lock 來鎖定 pthread_mutex_unlock 來解鎖,當使用完成后撇他,記得調(diào)用 pthread_mutex_destroy 來銷毀鎖茄猫。

pthread_mutex_init(&lock,NULL);
pthread_mutex_lock(&lock);
 //do your stuff
pthread_mutex_unlock(&lock);
pthread_mutex_destroy(&lock);

@synchronized
一個便捷的創(chuàng)建互斥鎖的方式,它做了其他互斥鎖所做的所有的事情困肩。

- (void)myMethod:(id)anObj
{
    @synchronized(anObj)
    {
        // Everything between the braces is protected by the @synchronized directive.
    }
}

自旋鎖(spinlock):
同樣用來標記只能有一個線程訪問該對象划纽,在同一線程多次加鎖操作會造成死鎖;使用硬件提供的swap指令或test_and_set指令實現(xiàn)锌畸;同互斥鎖不同的是在鎖操作需要等待的時候并不是睡眠等待喚醒勇劣,而是循環(huán)檢測保持者已經(jīng)釋放了鎖,這樣做的好處是節(jié)省了線程從睡眠狀態(tài)到喚醒之間內(nèi)核會產(chǎn)生的消耗潭枣,在加鎖時間短暫的環(huán)境下這點會提高很大效率

OSSpinLock
自旋鎖比默,和互斥鎖類似,都是為了保證線程安全的鎖卸耘。但二者的區(qū)別是不一樣的退敦,對于互斥鎖,當一個線程獲得這個鎖之后蚣抗,其他想要獲得此鎖的線程將會被阻塞侈百,直到該鎖被釋放瓮下。但自選鎖不一樣,當一個線程獲得鎖之后钝域,其他線程將會一直循環(huán)在哪里查看是否該鎖被釋放讽坏。所以,此鎖比較適用于鎖的持有者保存時間較短的情況下例证。

// 初始化
spinLock = OS_SPINLOCK_INIT;
// 加鎖
OSSpinLockLock(&spinLock);
// 解鎖
OSSpinLockUnlock(&spinLock);

os_unfair_lock
自旋鎖已經(jīng)不在安全路呜,然后蘋果又整出來個 os_unfair_lock_t (╯‵□′)╯︵┻━┻
這個鎖解決了優(yōu)先級反轉(zhuǎn)問題。

os_unfair_lock_t unfairLock;
unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock);
os_unfair_lock_unlock(unfairLock);

讀寫鎖(rwlock):
高級別鎖织咧,區(qū)分讀和寫胀葱,符合條件時允許多個線程訪問對象。處于讀鎖操作時可以允許其他線程和本線程的讀鎖笙蒙, 但不允許寫鎖抵屿, 處于寫鎖時則任何鎖操作都會睡眠等待;常見的操作系統(tǒng)會在寫鎖等待時屏蔽后續(xù)的讀鎖操作以防寫鎖被無限孤立而等待捅位,在操作系統(tǒng)不支持情況下可以用引用計數(shù)加寫優(yōu)先等待來用互斥鎖實現(xiàn)轧葛。 讀寫鎖適用于大量讀少量寫的環(huán)境,但由于其特殊的邏輯使得其效率相對普通的互斥鎖和自旋鎖要慢一個數(shù)量級艇搀;值得注意的一點是按POSIX標準 在線程申請讀鎖并未釋放前本線程申請寫鎖是成功的尿扯,但運行后的邏輯結(jié)果是無法預測
pthread_rwlock
讀寫鎖,在對文件進行操作的時候焰雕,寫操作是排他的衷笋,一旦有多個線程對同一個文件進行寫操作,后果不可估量矩屁,但讀是可以的右莱,多個線程讀取時沒有問題的。

  • 當讀寫鎖被一個線程以讀模式占用的時候档插,寫操作的其他線程會被阻塞,讀操作的其他線程還可以繼續(xù)進行亚再。
  • 當讀寫鎖被一個線程以寫模式占用的時候郭膛,寫操作的其他線程會被阻塞,讀操作的其他線程也被阻塞氛悬。
// 初始化
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER
// 讀模式
pthread_rwlock_wrlock(&lock);
// 寫模式
pthread_rwlock_rdlock(&lock);
// 讀模式或者寫模式的解鎖
pthread_rwlock_unlock(&lock);
 dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self readBookWithTag:1];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self readBookWithTag:2];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self writeBook:3];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self writeBook:4];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self readBookWithTag:5];
    });
- (void)readBookWithTag:(NSInteger )tag {
    pthread_rwlock_rdlock(&rwLock);
    NSLog(@"start read ---- %ld",tag);
    self.path = [[NSBundle mainBundle] pathForResource:@"1" ofType:@".doc"];
    self.contentString = [NSString stringWithContentsOfFile:self.path encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"end   read ---- %ld",tag);
    pthread_rwlock_unlock(&rwLock);
}
- (void)writeBook:(NSInteger)tag {
    pthread_rwlock_wrlock(&rwLock);
    NSLog(@"start wirte ---- %ld",tag);
    [self.contentString writeToFile:self.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
    NSLog(@"end   wirte ---- %ld",tag);
    pthread_rwlock_unlock(&rwLock);
}
start   read ---- 1
start   read ---- 2
end    read ---- 1
end    read ---- 2
start   wirte ---- 3
end    wirte ---- 3
start   wirte ---- 4
end    wirte ---- 4
start   read ---- 5
end    read ---- 5

遞歸鎖(recursivelock):
嚴格上講遞歸鎖只是互斥鎖的一個特例则剃,同樣只能有一個線程訪問該對象,但允許同一個線程在未釋放其擁有的鎖時反復對該鎖進行加鎖操作如捅; windows下的臨界區(qū)默認是支持遞歸鎖的棍现,而linux下的互斥量則需要設置參數(shù)PTHREAD_MUTEX_RECURSIVE_NP,默認則是不支持镜遣。

NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
void MyRecursiveFunction(int value)
{
    [theLock lock];
    if (value != 0)
    {
        --value;
        MyRecursiveFunction(value);
    }
    [theLock unlock];
}
MyRecursiveFunction(5);

條件鎖:
POSIX Conditions
POSIX 條件鎖需要互斥鎖和條件兩項來實現(xiàn)己肮,雖然看起來沒什么關系,但在運行時中,互斥鎖將會與條件結(jié)合起來谎僻。線程將被一個互斥和條件結(jié)合的信號來喚醒娄柳。
首先初始化條件和互斥鎖,當 ready_to_go 為 flase 的時候艘绍,進入循環(huán)赤拒,然后線程將會被掛起,直到另一個線程將 ready_to_go 設置為 true 的時候诱鞠,并且發(fā)送信號的時候挎挖,該線程會才被喚醒。

pthread_mutex_t mutex;
pthread_cond_t condition;
Boolean     ready_to_go = true;
void MyCondInitFunction()
{
    pthread_mutex_init(&mutex);
    pthread_cond_init(&condition, NULL);
}
void MyWaitOnConditionFunction()
{
    // Lock the mutex.
    pthread_mutex_lock(&mutex);
    // If the predicate is already set, then the while loop is bypassed;
    // otherwise, the thread sleeps until the predicate is set.
    while(ready_to_go == false)
    {
        pthread_cond_wait(&condition, &mutex);
    }
    // Do work. (The mutex should stay locked.)
    // Reset the predicate and release the mutex.
    ready_to_go = false;
    pthread_mutex_unlock(&mutex);
}
void SignalThreadUsingCondition()
{
    // At this point, there should be work for the other thread to do.
    pthread_mutex_lock(&mutex);
    ready_to_go = true;
    // Signal the other thread to begin work.
    pthread_cond_signal(&condition);
    pthread_mutex_unlock(&mutex);
}

信號量機制實現(xiàn)鎖
dispatch_semaphore
信號量機制實現(xiàn)鎖航夺,等待信號蕉朵,和發(fā)送信號,正如前邊所說的看門人一樣敷存,當有多個線程進行訪問的時候墓造,只要有一個獲得了信號,其他線程的就必須等待該信號釋放锚烦。

- (void)semphone:(NSInteger)tag {
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
    // do your stuff
    dispatch_semaphore_signal(semaphore);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末觅闽,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子涮俄,更是在濱河造成了極大的恐慌蛉拙,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件彻亲,死亡現(xiàn)場離奇詭異孕锄,居然都是意外死亡,警方通過查閱死者的電腦和手機苞尝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門畸肆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人宙址,你說我怎么就攤上這事轴脐。” “怎么了抡砂?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵大咱,是天一觀的道長。 經(jīng)常有香客問我注益,道長碴巾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任丑搔,我火速辦了婚禮厦瓢,結(jié)果婚禮上提揍,老公的妹妹穿的比我還像新娘。我一直安慰自己旷痕,他們只是感情好碳锈,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著欺抗,像睡著了一般售碳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绞呈,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天贸人,我揣著相機與錄音,去河邊找鬼佃声。 笑死艺智,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的圾亏。 我是一名探鬼主播十拣,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼志鹃!你這毒婦竟也來了夭问?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤曹铃,失蹤者是張志新(化名)和其女友劉穎缰趋,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陕见,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡秘血,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了评甜。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灰粮。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖忍坷,靈堂內(nèi)的尸體忽然破棺而出谋竖,到底是詐尸還是另有隱情,我是刑警寧澤承匣,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站锤悄,受9級特大地震影響韧骗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜零聚,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一袍暴、第九天 我趴在偏房一處隱蔽的房頂上張望些侍。 院中可真熱鬧,春花似錦政模、人聲如沸岗宣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耗式。三九已至,卻和暖如春趁猴,著一層夾襖步出監(jiān)牢的瞬間刊咳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工儡司, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留娱挨,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓捕犬,卻偏偏與公主長得像跷坝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子碉碉,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容