1. 程式人生 > >iOS訊號量的使用

iOS訊號量的使用

那你在程式碼中是否很好的使用了鎖的機制呢?你又知道幾種實現鎖的方法呢?

今天一起來探討一下Objective-C中幾種不同方式實現的鎖,在這之前我們先構建一個測試用的類,假想它是我們的一個共享資源,method1與method2是互斥的,程式碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 @implementationTestObj -(void)method1 {   NSLog(@"%@",NSStringFromSelector(_cmd)); } -(void)method2
{   NSLog(@"%@",NSStringFromSelector(_cmd)); } @end

1.使用NSLock實現的鎖

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19     //主執行緒中     TestObj *obj=[[TestObjalloc]init];     NSLock *lock=[[NSLockalloc]init];     //執行緒1     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
        [locklock];         [objmethod1];         sleep(10);         [lockunlock];     });     //執行緒2     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         sleep(1);//以保證讓執行緒2的程式碼後執行         [locklock];         [objmethod2];         [lockunlock];     });

看到列印的結果了嗎,你會看到執行緒1鎖住之後,執行緒2會一直等待走到執行緒1將鎖置為unlock後,才會執行method2方法。

NSLock是Cocoa提供給我們最基本的鎖物件,這也是我們經常所使用的,除lock和unlock方法外,NSLock還提供了tryLock和lockBeforeDate:兩個方法,前一個方法會嘗試加鎖,如果鎖不可用(已經被鎖住),剛並不會阻塞執行緒,並返回NO。lockBeforeDate:方法會在所指定Date之前嘗試加鎖,如果在指定時間之前都不能加鎖,則返回NO。

2.使用synchronized關鍵字構建的鎖

當然在Objective-C中你還可以用@synchronized指令快速的實現鎖:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18     //主執行緒中     TestObj *obj=[[TestObjalloc]init];     //執行緒1     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         @synchronized(obj){             [objmethod1];             sleep(10);         }     });     //執行緒2     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         sleep(1);         @synchronized(obj){             [objmethod2];         }     });

@synchronized指令使用的obj為該鎖的唯一標識,只有當標識相同時,才為滿足互斥,如果執行緒2中的@synchronized(obj)改為@synchronized(other),剛執行緒2就不會被阻塞,@synchronized指令實現鎖的優點就是我們不需要在程式碼中顯式的建立鎖物件,便可以實現鎖的機制,但作為一種預防措施,@synchronized塊會隱式的新增一個異常處理例程來保護程式碼,該處理例程會在異常丟擲的時候自動的釋放互斥鎖。所以如果不想讓隱式的異常處理例程帶來額外的開銷,你可以考慮使用鎖物件。

3.使用C語言的pthread_mutex_t實現的鎖

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21     //主執行緒中     TestObj *obj=[[TestObjalloc]init];     __blockpthread_mutex_tmutex;     pthread_mutex_init(&mutex,NULL);     //執行緒1     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         pthread_mutex_lock(&mutex);         [objmethod1];         sleep(5);         pthread_mutex_unlock(&mutex);     });     //執行緒2     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         sleep(1);         pthread_mutex_lock(&mutex);         [objmethod2];         pthread_mutex_unlock(&mutex);     });

pthread_mutex_t定義在pthread.h,所以記得#include <pthread.h>
4.使用GCD來實現的”鎖”
以上程式碼構建多執行緒我們就已經用到了GCD的dispatch_async方法,其實在GCD中也已經提供了一種訊號機制,使用它我們也可以來構建一把”鎖”(從本質意義上講,訊號量與鎖是有區別,具體差異參加訊號量與互斥鎖之間的區別):

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19     //主執行緒中     TestObj *obj=[[TestObjalloc]init];     dispatch_semaphore_tsemaphore=dispatch_semaphore_create(1);     //執行緒1     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);         [objmethod1];         sleep(10);         dispatch_semaphore_signal(semaphore);     });     //執行緒2     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         sleep(1);         dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);         [objmethod2];         dispatch_semaphore_signal(semaphore);     });

至於程式碼產生的效果當然和上一例是一模一樣的,關於訊號機制,熟悉C程式設計的你肯定也不會陌生的,關於GCD中更多關於dispatch_semaphore_t的資訊,可以跳轉到本部落格的這一往篇文章:GCD介紹(三): Dispatch Sources

好了,以上就是我所列舉了幾種方式來實現鎖,當然鎖大多數情況下也是配合多執行緒一起使用的,關於多執行緒程式設計,我這兒就不贅述了。

在上一文中,我們已經討論過用Objective-C鎖幾種實現(跳轉地址),也用程式碼實際的演示瞭如何通過構建一個互斥鎖來實現多執行緒的資源共享及執行緒安全,今天我們繼續討論鎖的一些高階用法。

1.NSRecursiveLock遞迴鎖

平時我們在程式碼中使用鎖的時候,最容易犯的一個錯誤就是造成死鎖,而容易造成死鎖的一種情形就是在遞迴或迴圈中,如下程式碼:

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     //主執行緒中     NSLock *theLock=[[NSLockalloc]init];     TestObj *obj=[[TestObjalloc]init];     //執行緒1     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         staticvoid(^TestMethod)(int);         TestMethod=^(intvalue)         {             [theLocklock];             if(value>0)             {                 [objmethod1];                 sleep(5);                 TestMethod(value-1);             }             [theLockunlock];         };         TestMethod(5);     });     //執行緒2     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         sleep(1);         [theLocklock];         [objmethod2];         [theLockunlock];     });

以上的程式碼中,就是一種典型的死鎖情況,因為線上程1中的遞迴block中,鎖會被多次的lock,所以自己也被阻塞了,由於以上的程式碼非常的簡短,所以很容易能識別死鎖,但在較為複雜的程式碼中,就不那麼容易發現了,那麼如何在遞迴或迴圈中正確的使用鎖呢?此處的theLock如果換用NSRecursiveLock物件,問題便得到解決了,NSRecursiveLock類定義的鎖可以在同一執行緒多次lock,而不會造成死鎖。遞迴鎖會跟蹤它被多少次lock。每次成功的lock都必須平衡呼叫unlock操作。只有所有的鎖住和解鎖操作都平衡的時候,鎖才真正被釋放給其他執行緒獲得。

2.NSConditionLock條件鎖

當我們在使用多執行緒的時候,有時一把只會lock和unlock的鎖未必就能完全滿足我們的使用。因為普通的鎖只能關心鎖與不鎖,而不在乎用什麼鑰匙才能開鎖,而我們在處理資源共享的時候,多數情況是隻有滿足一定條件的情況下才能開啟這把鎖:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20     //主執行緒中     NSConditionLock *theLock=[[NSConditionLockalloc]init];     //執行緒1     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         for(inti=0;i<=2;i++)         {             [theLocklock];             NSLog(@"thread1:%d",i);             sleep(2);             [theLockunlockWithCondition:i];         }     });     //執行緒2     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         [theLocklockWhenCondition:2];         NSLog(@"thread2");         [theLockunlock];     });

線上程1中的加鎖使用了lock,所以是不需要條件的,所以順利的就鎖住了,但在unlock的使用了一個整型的條件,它可以開啟其它執行緒中正在等待這把鑰匙的臨界地,而執行緒2則需要一把被標識為2的鑰匙,所以當執行緒1迴圈到最後一次的時候,才最終打開了執行緒2中的阻塞。但即便如此,NSConditionLock也跟其它的鎖一樣,是需要lock與unlock對應的,只是lock,lockWhenCondition:與unlock,unlockWithCondition:是可以隨意組合的,當然這是與你的需求相關的。

3.NSDistributedLock分散式鎖

以上所有的鎖都是在解決多執行緒之間的衝突,但如果遇上多個程序或多個程式之間需要構建互斥的情景該怎麼辦呢?這個時候我們就需要使用到NSDistributedLock了,從它的類名就知道這是一個分散式的Lock,NSDistributedLock的實現是通過檔案系統的,所以使用它才可以有效的實現不同程序之間的互斥,但NSDistributedLock並非繼承於NSLock,它沒有lock方法,它只實現了tryLock,unlock,breakLock,所以如果需要lock的話,你就必須自己實現一個tryLock的輪詢,下面通過程式碼簡單的演示一下吧:

程式A:

1 2 3 4 5 6 7 8     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         lock=[[NSDistributedLockalloc]initWithPath:@"/Users/mac/Desktop/earning__"];         [lockbreakLock];         [locktryLock];         sleep(10);         [lockunlock];         NSLog(@"appA: OK");     });

程式B:

1 2 3 4 5 6 7 8 9 10 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{         lock=[[NSDistributedLockalloc]initWithPath:@"/Users/mac/Desktop/earning__"];         while(![locktryLock]){             NSLog(@"appB: waiting");             sleep(1);         }         [lockunlock];         NSLog(@"appB: OK");     });

先執行程式A,然後立即執行程式B,根據列印你可以清楚的發現,當程式A剛執行的時候,程式B一直處於等待中,當大概10秒過後,程式B便打印出了appB:OK的輸出,以上便實現了兩上不同程式之間的互斥。/Users/mac/Desktop/earning__是一個檔案或資料夾的地址,如果該檔案或資料夾不存在,那麼在tryLock返回YES時,會自動建立該檔案/資料夾。在結束的時候該檔案/資料夾會被清除,所以在選擇的該路徑的時候,應該選擇一個不存在的路徑,以防止誤刪了檔案。

相關推薦

iOS訊號的使用

那你在程式碼中是否很好的使用了鎖的機制呢?你又知道幾種實現鎖的方法呢? 今天一起來探討一下Objective-C中幾種不同方式實現的鎖,在這之前我們先構建一個測試用的類,假想它是我們的一個共享資源,method1與method2是互斥的,程式碼如下: 1 2 3

iOS訊號機制

記得之前有講解過iOS多執行緒的處理,當時使用GCD的時候還有很多沒有講太清楚的地方,今天做一個小小的補充: 使用NSOperationQueue可以控制併發執行緒,但是如何在GCD中快速的控制併發呢?這裡就需要使用到訊號量(dispatch_semaphor

iOS GCD中級篇 - dispatch_semaphore(訊號)的理解及使用

理解這個概念之前,先丟擲一個問題 問題描述: 假設現在系統有兩個空閒資源可以被利用,但同一時間卻有三個執行緒要進行訪問,這種情況下,該如何處理呢? 或者 我們要下載很多圖片,併發非同步進行,每個下載都會開闢一個新執行緒,可是我們又擔心太多執行緒肯定cpu吃不消,那麼我們這裡也可以用訊號量控制

ios GCD之訊號機制

一,什麼是訊號量機制    抽象說:     訊號量是一個整型值並且具有一個初始計數值,並且支援兩個操作:訊號通知和等待。當一個訊號量被訊號通知,其計數會被增加。當一個執行緒在一個訊號量上等待時,假如此時的訊號量的整型值為0, 執行緒會被阻塞(因為等待會讓訊號量的整型

iOS之利用GCD訊號控制併發網路請求

引對計算機瞭解的都會知道訊號量的作用,當我們多個執行緒要訪問同一個資源的時候,往往會設定一個訊號量,當訊號量大於0的時候,新的執行緒可以去操作這個資源,操作時訊號量-1,操作完後訊號量+1,當訊號量等於0的時候,必須等待,所以通過控制訊號量,我們可以控制能夠同時進行的併發數。

iOS控制高併發-dispatch_semaphore(訊號)的用法

1.前言:     在單執行緒的程式中,程式的執行有先後順序,但是在多執行緒的程式中,線上程之間的程式執行中沒有先後順序,因此會出現併發的情況,導致程式的不確定性,因此需要dispatch_semaphore做程式的高併發管理。2.介紹:    dispatch_sema

iOS 通過新增執行緒依賴和訊號結合實現一個複雜介面請求多個介面時按指定順序執行

前言:本文通過作者的一種思想實現關於iOS中一個介面請求多條資料時,需要按指定順序執行 主要思路:為了實現多個請求介面按指定順序前後執行,本文主要通過新增執行緒依賴關係和GCD的訊號量相結合來實現基本需求貼上Demo地址,感覺可以的麻煩記得點個星星。

iOS系統GCD學習(5):訊號機制

當我們在處理一系列執行緒的時候,當數量達到一定量,在以前我們可能會選擇使用NSOperationQueue來處理併發控制,但如何在GCD中快速的控制併發呢?答案就是dispatch_semaphore,對經常做unix開發的人來講,我所介紹的內容可能就顯得非常入門級了,訊

IOS多執行緒使用GCD與訊號實現生產者與消費者模式

一、原理的簡述   在生產者消費者模式當中,首先需要分清在這個模式當中有哪些角色? 各角色分別擔任什麼職責與它們之間的關係如何? 角色之間是在保證資料的準確性的情況下如何通訊(同步資料)的? 假設現在有一個這樣的情形: 有兩個人共同訪問一個容量有限的倉庫,這2個人,

[領卓教育]執行緒的同步與互斥機制——訊號

訊號量的初始化 int sem_init(sem_t *sem, int pshared, unsigned int value); 功能: 初始化訊號量 引數: sem :要是初始化的訊號量  pshared: 訊號量共享的範圍(0: 執行緒間使用 非0:程序間使用) value : 初始

作業系統 自旋鎖+訊號+互斥+臨界區+死鎖的區別

自旋鎖(SpinLock) 自旋鎖是專為防止多處理器併發而引入的一種鎖。如果是單核處理器,則自旋鎖定義為空操作,因為簡單的關閉中斷即可實現互斥。   自旋鎖最多隻能被一個執行緒持有,如果一個執行緒試圖請求一個已被爭用(已被另一個執行緒持有)的自旋鎖,那麼等待自旋鎖的執行緒將會反

多執行緒,訊號的簡單使用 GCD

基本概念 關於iOS開發中,多執行緒基本的概念和基本使用,我在這裡就不在重複說了。但是為了照顧到有的同學可能還不是對基本的概念不熟悉,可以參考一下這篇文章併發其實很簡單 說說訊號量,併發數 如果你有計算機基礎,那麼下面這段話應該很簡單就能理解 訊號量就是一個資源計數器,對訊號

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

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

【Nginx】訊號

TERM, INT Quick shutdown     QUIT Graceful shutdown 優雅的關閉程序,即等請求結束後再關閉 KILL

linux 多執行緒之訊號 sem_init

1. 什麼是訊號量 linux sem 訊號量是一種特殊的變數,訪問具有原子性, 用於解決程序或執行緒間共享資源引發的同步問題。 使用者態程序對 sem 訊號量可以有以下兩種操作: 等待訊號量 當訊號量值為 0 時,程式等待;當訊號量值大於 0 時,訊號量減 1,程式

Linux基礎(四)——訊號與PV操作

在計算機作業系統中,PV操作是程序管理中的難點。1、基本含義      什麼是訊號量?訊號量(semaphore)的資料結構為一個值和一個指標,指標指向等待該訊號量的下一個程序。訊號量的值與相應資源的使用情況有關。當它的值大於0時,表示當前可用資源的

PYTHON——多執行緒:訊號(Semaphore)

  訊號量也是一把鎖,用來控制執行緒併發數的。   BoundedSemaphore或Semaphore管理一個內建的計數 器,每當呼叫acquire()時-1,呼叫release()時+1。       計數器不能小於0,當計數器為 0時,acquire()將阻塞執行緒至同

【Qt開發】QThread中的互斥、讀寫鎖、訊號、條件變數

在gemfield的《從pthread到QThread》一文中我們瞭解了執行緒的基本使用,但是有一大部分的內容當時說要放到這片文章裡討論,那就是執行緒的同步問題。關於這個問題,gemfield在《從進 程到執行緒》中有一個比喻,有必要重新放在下面溫習下: ***************

【OS】訊號機制

儲存一下自己看,侵刪。 原文地址:http://blog.csdn.net/speedme/article/details/17597373 上篇部落格中(程序同步之臨界區域問題及Peterson演算法),我們對臨界區,臨界資源,鎖機制詳細解讀了下,留下了一個問題,就是鎖機制只能判斷臨界資源是否被佔用,所

【Windows原理】執行緒同步-訊號

#include "stdafx.h" #include <windows.h> int g_num = 0; HANDLE g_hSemaphore = nullptr; DWORD WINAPI ThreadProc(LPVOID lpParam) { for