Objective-C中的鎖

原文地址:http://blog.sina.com.cn/s/blog_72819b170101590n.html


 鎖是線程編程同步工具的基礎。鎖可以讓你很容易保護代碼中一大塊區域以便你可以確保代碼的正確性。

1 使用POSIX互斥鎖
    POSIX互斥鎖在很多程序裏面很容易使用。爲了新建一個互斥鎖,你聲明並初始化一個pthread_mutex_t的結構。爲了鎖住和解鎖一個互斥鎖,你可以使用pthread_mutex_lock和pthread_mutex_unlock函數。列表4-2顯式了要初始化並使用一個POSIX線程的互斥鎖的基礎代碼。當你用完一個鎖之後,只要簡單的調用pthread_mutex_destroy來釋放該鎖的數據結構。 
    Listing 4-2 
    Using a mutex lock pthread_mutex_t mutex; 
    void MyInitFunction() 
    
        pthread_mutex_init(&mutex, NULL); 
    
    void MyLockingFunction() 
    
        pthread_mutex_lock(&mutex); 
        // Do work. pthread_mutex_unlock(&mutex); 
    }
    注意:上面的代碼只是簡單的顯式了使用一個POSIX線程互斥鎖的步驟。你自己的代碼應該檢查這些函數返回的錯誤碼,並適當的處理它們。
4.6.2 使用NSLock類
    在Cocoa程序中NSLock中實現了一個簡單的互斥鎖。所有鎖(包括NSLock)的接口實際上都是通過NSLocking協議定義的,它定義了lock和unlock方法。你使用這些方法來獲取和釋放該鎖。
    除了標準的鎖行爲,NSLock類還增加了tryLock和lockBeforeDate:方法。方法tryLock試圖獲取一個鎖,但是如果鎖不可用的時候,它不會阻塞線程。相反,它只是返回NO。而lockBeforeDate:方法試圖獲取一個鎖,但是如果鎖沒有在規定的時間內被獲得,它會讓線程從阻塞狀態變爲非阻塞狀態(或者返回NO)。
    下面的例子顯式了你可以是NSLock對象來協助更新一個可視化顯式,它的數據結構被多個線程計算。如果線程沒有立即獲的鎖,它只是簡單的繼續計算直到它可以獲得鎖再更新顯式。 
    BOOL moreToDo = YES; 
    NSLock *theLock = [[NSLock alloc] init]; 
    ... 
    while (moreToDo) { 
         
         
        if ([theLock tryLock]) { 
             
            [theLock unlock]; 
        
    }
4.6.3 使用@synchronized指令
    @synchronized指令是在Objective-C代碼中創建一個互斥鎖非常方便的方法。@synchronized指令做和其他互斥鎖一樣的工作(它防止不同的線程在同一時間獲取同一個鎖)。然而在這種情況下,你不需要直接創建一個互斥鎖或鎖對象。相反,你只需要簡單的使用Objective-C對象作爲鎖的令牌,如下面例子所示: 
    - (void)myMethod:(id)anObj 
    
        @synchronized(anObj) 
        
            // Everything between the braces is protected by the @synchronized directive. 
        
    }
    創建給@synchronized指令的對象是一個用來區別保護塊的唯一標示符。如果你在兩個不同的線程裏面執行上述方法,每次在一個線程傳遞了一個不同的對象給anObj參數,那麼每次都將會擁有它的鎖,並持續處理,中間不被其他線程阻塞。然而,如果你傳遞的是同一個對象,那麼多個線程中的一個線程會首先獲得該鎖,而其
他線程將會被阻塞直到第一個線程完成它的臨界區。
    作爲一種預防措施,@synchronized塊隱式的添加一個異常處理例程來保護代碼。該處理例程會在異常拋出的時候自動的釋放互斥鎖。這意味着爲了使用@synchronized指令,你必須在你的代碼中啓用異常處理。瞭如果你不想讓隱式的異常處理例程帶來額外的開銷,你應該考慮使用鎖的類。
4.6.4 使用其他Cocoa鎖
    以下個部分描述了使用Cocoa其他類型的鎖。
·使用NSRecursiveLock對象
    NSRecursiveLock類定義的鎖可以在同一線程多次獲得,而不會造成死鎖。一個遞歸鎖會跟蹤它被多少次成功獲得了。每次成功的獲得該鎖都必須平衡調用鎖住和解鎖的操作。只有所有的鎖住和解鎖操作都平衡的時候,鎖才真正被釋放給其他線程獲得。
    正如它名字所言,這種類型的鎖通常被用在一個遞歸函數裏面來防止遞歸造成阻塞線程。你可以類似的在非遞歸的情況下使用他來調用函數,這些函數的語義要求它們使用鎖。以下是一個簡單遞歸函數,它在遞歸中獲取鎖。如果你不在該代碼裏使用NSRecursiveLock對象,當函數被再次調用的時候線程將會出現死鎖。               NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init]; 
    void MyRecursiveFunction(int value) { 
        [theLock lock]; 
        if (value != 0) { 
            --value; 
            MyRecursiveFunction(value); 
        }
        [theLock unlock]; 
    
    MyRecursiveFunction(5);
    注意:因爲一個遞歸鎖不會被釋放直到所有鎖的調用平衡使用瞭解鎖操作,所以你必須仔細權衡是否決定使用鎖對性能的潛在影響。長時間持有一個鎖將會導致其他線程阻塞直到遞歸完成。如果你可以重寫你的代碼來消除遞歸或消除使用一個遞歸鎖,你可能會獲得更好的性能。
·使用NSConditionLock對象
    NSConditionLock對象定義了一個互斥鎖,可以使用特定值來鎖住和解鎖。不要把該類型的鎖和條件(參見“條件”部分)混淆了。它的行爲和條件有點類似,但是它們的實現非常不同。
    通常,當多線程需要以特定的順序來執行任務的時候,你可以使用一個NSConditionLock對象,比如當一個線程生產數據,而另外一個線程消費數據。生產者執行時,消費者使用由你程序指定的條件來獲取鎖(條件本身是一個你定義的整形值)。當生產者完成時,它會解鎖該鎖並設置鎖的條件爲合適的整形值來喚醒消費者線程,之後消費線程繼續處理數據。
    NSConditionLock的鎖住和解鎖方法可以任意組合使用。比如,你可以使用unlockWithCondition:和lock消息,或使用lockWhenCondition:和unlock消息。當然,後面的組合可以解鎖一個鎖但是可能沒有釋放任何等待某特定條件值的線程。
    下面的例子顯示了生產者-消費者問題如何使用條件鎖來處理。想象一個應用程序包含一個數據的隊列。一個生產者線程把數據添加到隊列,而消費者線程從隊列中取出數據。生產者不需要等待特定的條件,但是它必須等待鎖可用以便它可以安全的把數據添加到隊列。 
    id condLock = [[NSConditionLock alloc] initWithCondition:NO_DATA]; 
    while(true) { 
        [condLock lock]; 
        
        [condLock unlockWithCondition:HAS_DATA]; 
    }
    因爲初始化條件鎖的值爲NO_DATA,生產者線程在初始化的時候可以毫無問題的獲取該鎖。它會添加隊列數據,並把條件設置爲HAS_DATA。在隨後的迭代中,生產者線程可以把到達的數據添加到隊列,無論隊列是否爲空或依然有數據。唯一讓它進入阻塞的情況是當一個消費者線程充隊列取出數據的時候。
    因爲消費者線程必須要有數據來處理,它會使用一個特定的條件來等待隊列。當生產者把數據放入隊列時,消費者線程被喚醒並獲取它的鎖。它可以從隊列中取出數據,並更新隊列的狀態。下列代碼顯示了消費者線程處理循環的基本結構。 
    while (true) { 
        [condLock lockWhenCondition:HAS_DATA]; 
         
        [condLock unlockWithCondition:(isEmpty ? NO_DATA : HAS_DATA)]; 
        // Process the data locally. 
    }
·使用NSDistributedLock對象
    NSDistributedLock類可以被多臺主機上的多個應用程序使用來限制對某些共享資源的訪問,比如一個文件。鎖本身是一個高效的互斥鎖,它使用文件系統項目來實現,比如一個文件或目錄。對於一個可用的NSDistributedLock對象,鎖必須由所有使用它的程序寫入。這通常意味着把它放在文件系統,該文件系統可以被所有運行在計算機上面的應用程序訪問。
    不像其他類型的鎖,NSDistributedLock並沒有實現NSLocking協議,所有它沒有lock方法。一個lock方法將會阻塞線程的執行,並要求系統以預定的速度輪詢鎖。以其在你的代碼中實現這種約束,NSDistributedLock提供了一個tryLock方法,並讓你決定是否輪詢。
    因爲它使用文件系統來實現,一個NSDistributedLock對象不會被釋放除非它的擁有者顯式的釋放它。如果你的程序在用戶一個分佈鎖的時候崩潰了,其他客戶端無法訪問該受保護的資源。在這種情況下,你可以使用breadLock方法來打破現存的鎖以便你可以獲取它。但是通常應該避免打破鎖,除非你確定擁有進程已經死亡並不可能再釋放該鎖。
    和其他類型的鎖一樣,當你使用NSDistributedLock對象時,你可以通過調用unlock方法來釋放它。

發佈了11 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章