1. 程式人生 > >iOS開發-多執行緒開發之執行緒安全篇

iOS開發-多執行緒開發之執行緒安全篇

前言:一塊資源可能會被多個執行緒共享,也就是多個執行緒可能會訪問同一塊資源,比如多個執行緒訪問同一個物件、同一個變數、同一個檔案和同一個方法等。因此當多個執行緒訪問同一塊資源時,很容易會發生資料錯誤及資料不安全等問題。因此要避免這些問題,我們需要使用“執行緒鎖”來實現。

一、使用關鍵字

優點:使用@synchronized關鍵字可以很方便地建立鎖物件,而且不用顯式的建立鎖物件。

缺點:會隱式新增一個異常處理來保護程式碼,該異常處理會在異常丟擲的時候自動釋放互斥鎖。而這種隱式的異常處理會帶來系統的額外開銷,為優化資源,你可以使用鎖物件。

二、Object-C”語言

條件鎖,遞迴或迴圈方法時使用此方法實現鎖,可避免死鎖等問題。

使用此方法可以指定,只有滿足條件的時候才可以解鎖。

4)NSDistributedLock(分散式鎖)

在IOS中不需要用到,也沒有這個方法,因此本文不作介紹,這裡寫出來只是想讓大家知道有這個鎖存在。

如果想要學習NSDistributedLock的話,你可以建立MAC OS的專案自己演練,方法請自行Google,謝謝。

三、C語言

執行緒安全 —— 鎖

一、使用關鍵字

1)@synchronized

// 例項類person
Person *person = [[Person alloc] init];

// 執行緒A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(person) {
        [person personA];
        [NSThread sleepForTimeInterval:3]; // 執行緒休眠3秒
    }
});

// 執行緒B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(person) {
        [person personB];
    }
});

關鍵字@synchronized的使用,鎖定的物件為鎖的唯一標識,只有標識相同時,才滿足互斥。如果執行緒B鎖物件person改為self或其它標識,那麼執行緒B將不會被阻塞。你是否看到@synchronized(self) ,也是對的。它可以鎖任何物件,描述為@synchronized(anObj)。

二、Object-C語言

1)使用NSLock實現鎖

// 例項類person
Person *person = [[Person alloc] init];
// 建立鎖
NSLock *myLock = [[NSLock alloc] init];

// 執行緒A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0
), ^{ [myLock lock]; [person personA]; [NSThread sleepForTimeInterval:5]; [myLock unlock]; }); // 執行緒B dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [myLock lock]; [person personB]; [myLock unlock]; }); 

程式執行結果:執行緒B會等待執行緒A解鎖後,才會去執行執行緒B。如果執行緒Blockunlock方法去掉之後,則執行緒B不會被阻塞,這個和synchronized的一樣,需要使用同樣的鎖物件才會互斥。

NSLock類還提供tryLock方法,意思是嘗試鎖定,當鎖定失敗時,不會阻塞程序,而是會返回NO。你也可以使用lockBeforeDate:方法,意思是在指定時間之前嘗試鎖定,如果在指定時間前都不能鎖定,也是會返回NO

注意:鎖定(lock)和解鎖(unLock)必須配對使用

2)使用NSRecursiveLock類實現鎖 

// 例項類person
Person *person = [[Person alloc] init];
// 建立鎖物件
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];

// 建立遞迴方法
static void (^testCode)(int);
testCode = ^(int value) {
    [theLock tryLock];
    if (value > 0)
    {
        [person personA];
        [NSThread sleepForTimeInterval:1];
        testCode(value - 1);
    }
    [theLock unlock];
};

//執行緒A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    testCode(5);
});

//執行緒B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [theLock lock];
    [person personB];
    [theLock unlock];
});

如果我們把NSRecursiveLock類換成NSLock類,那麼程式就會死鎖。因為在此例子中,遞迴方法會造成鎖被多次鎖定(Lock),所以自己也被阻塞了。而使用NSRecursiveLock類,則可以避免這個問題。

3)使用NSConditionLock(條件鎖)類實現鎖:

使用此方法可以建立一個指定開鎖的條件,只有滿足條件,才能開鎖。

// 例項類person
Person *person = [[Person alloc] init];
// 建立條件鎖
NSConditionLock *conditionLock = [[NSConditionLock alloc] init];

// 執行緒A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [conditionLock lock];
    [person personA];
    [NSThread sleepForTimeInterval:5];
    [conditionLock unlockWithCondition:10];
});

// 執行緒B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [conditionLock lockWhenCondition:10];
    [person personB];
    [conditionLock unlock];
});

執行緒A使用的是lock方法,因此會直接進行鎖定,並且指定了只有滿足10的情況下,才能成功解鎖。

unlockWithCondition:方法,建立條件鎖,引數傳入“整型”。lockWhenCondition:方法,則為解鎖,也是傳入一個“整型”的引數。

三、C語言

1)使用pthread_mutex_t實現鎖

注意:必須在標頭檔案匯入:#import <pthread.h>

// 例項類person
Person *person = [[Person alloc] init];

// 建立鎖物件
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

// 執行緒A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personA];
    [NSThread sleepForTimeInterval:5];
    pthread_mutex_unlock(&mutex);
});

// 執行緒B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personB];
    pthread_mutex_unlock(&mutex);
});

實現效果和上例的相一致

2)使用GCD實現(訊號量)

GCD提供一種訊號的機制,使用它我們可以建立“鎖”(訊號量和鎖是有區別的,具體請自行百度)。

// 例項類person
Person *person = [[Person alloc] init];

// 建立並設定信量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

// 執行緒A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [person personA];
    [NSThread sleepForTimeInterval:5];
    dispatch_semaphore_signal(semaphore);
});

// 執行緒B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    [person personB];
    dispatch_semaphore_signal(semaphore);
});

效果也是和上例介紹的相一致。

我在這裡解釋一下程式碼。dispatch_semaphore_wait方法是把訊號量加1,dispatch_semaphore_signal是把訊號量減1。

我們把訊號量當作是一個計數器,當計數器是一個非負整數時,所有通過它的執行緒都應該把這個整數減1。如果計數器大於0,那麼則允許訪問,並把計數器減1。如果為0,則訪問被禁止,所有通過它的執行緒都處於等待的狀態。

3)使用POSIX(條件鎖)建立鎖

// 例項類person
Person *person = [[Person alloc] init];

// 建立互斥鎖
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
// 建立條件鎖
__block pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

// 執行緒A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    pthread_cond_wait(&cond, &mutex);
    [person personA];
    pthread_mutex_unlock(&mutex);
});

// 執行緒B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    pthread_mutex_lock(&mutex);
    [person personB];
    [NSThread sleepForTimeInterval:5];
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
});

效果:程式會首先呼叫執行緒B,在5秒後再呼叫執行緒A。因為線上程A中建立了等待條件鎖,執行緒B有啟用鎖,只有當執行緒B執行完後會啟用執行緒A。

pthread_cond_wait方法為等待條件鎖。

pthread_cond_signal方法為激動一個相同條件的條件鎖。

簡單總結:

一般來說,如果專案不大,我們都會偷點懶,直接使用關鍵字@synchronized建立鎖,懶人方法。其次可以使用蘋果提供的OC方法,最後才會去使用C去建立鎖。

本文參考文章:

博文作者:GarveyCalvin

本文版權歸作者和部落格園共有,歡迎轉載,但須保留此段宣告,並給出原文連結,謝謝合作!

相關推薦

iOS開發執行開發執行全篇

前言:一塊資源可能會被多個執行緒共享,也就是多個執行緒可能會訪問同一塊資源,比如多個執行緒訪問同一個物件、同一個變數、同一個檔案和同一個方法等。因此當多個執行緒訪問同一塊資源時,很容易會發生資料錯誤及資料不安全等問題。因此要避免這些問題,我們需要使用“執行緒鎖”來實現。 一、使用關鍵字 優

iOS開發執行程式設計技術(Thread、Cocoa operations、GCD)

簡介 在軟體開發中,多執行緒程式設計技術被廣泛應用,相信多執行緒任務對我們來說已經不再陌生了。有了多執行緒技術,我們可以同做多個事情,而不是一個一個任務地進行。比如:前端和後臺作互動、大任務(需要耗費一定的時間和資源)等等。也就是說,我們可以使用執行緒把佔據時間長的任務放到後臺中處理,而不影響到使用者的使用

Java執行-併發執行

執行緒池有了解嗎? 答: java.util.concurrent.ThreadPoolExecutor 類就是一個執行緒池。客戶端呼叫ThreadPoolExecutor.submit(Runnable task) 提交任務,執行緒池內部維護的工作者執行緒的數量就是該執行緒池的執行

Java執行-併發執行和程序的區別

執行緒和程序的區別 答: 程序是一個“執行中的程式”,是系統進行資源分配和排程的一個獨立單位 執行緒是程序的一個實體,一個程序中擁有多個執行緒,執行緒之間共享地址空間和其他資源(所以通訊和同步等操作執行緒比程序更加容易) 執行緒上下文的切換比程序上下文切換要快

Java 進階——執行優化執行池 ThreadPoolExecutor的核心容器阻塞佇列詳解(一)

#引言 多執行緒我想無論是後端開發,還是對於App開發者來說都不會陌生,何況Android強制要求不能在主執行緒中做網路請求,於是乎,在很多初學者或者App的原始碼中會出現會多的new Thread…的方式,這樣的程式碼是不優雅而且存在很多的隱患,假如說在使用者

執行程式設計執行基礎

前言 此內容是閱讀了書籍《JAVA多執行緒程式設計核心技術》後作為學習總結的文章,同時也梳理一下內容。建議大家有興趣都可以閱讀一下這本書,對於想了解更多的同學來說是一個很好的教材,同時建議大家多去思考和動手編寫程式碼,融會貫通之後再去看一遍,會有更多的體會。就比如《JVM底層實現最佳實戰》的書籍一樣,我讀了

Java執行基礎執行特性

核心知識: ① 使用多執行緒技術時,程式碼的執行結果與程式碼執行的順序或呼叫順序是無關的 public class MyThread extends Thread{ @Override public void run() { super.run(); System.o

Java執行總結執行安全佇列Queue

在Java多執行緒應用中,佇列的使用率很高,多數生產消費模型的首選資料結構就是佇列。Java提供的執行緒安全的Queue可以分為阻塞佇列和非阻塞佇列,其中阻塞佇列的典型例子是BlockingQueue,非阻塞佇列的典型例子是ConcurrentLinkedQueue,在實際

Java執行學習執行組、執行池的使用

執行緒組 Java中使用ThreadGroup來表示執行緒組,可以對一批執行緒進行分類管理。 package thread; public class MyRunnable implements Runnable { @Override public void run() { fo

併發與執行基礎執行之間共享資料

1、共享資料帶來什麼問題?        A、條件競爭:併發中競爭條件的形成,取決於一個以上執行緒的相對執行順序,每個執行緒都搶著完成自己的任務。大多數情況下,即使改變執行順序,也是良性競爭,其結果可以接受。例如,有兩個執行緒同時向一個處理佇列中新增任務,因為系統提供的不變數

執行程式設計執行間的通訊——管道通訊

上一章節講了wait/notify通訊,這一節我們來探討使用管道進行通訊。 java中提供了IO流使我們很方便的對資料進行操作,pipeStream是一種特殊的流,用於不同執行緒間直接傳送資料。一個執行緒將資料傳送到輸出管道,另一個執行緒從輸入管道讀取資料。通

Linux程式設計學習筆記----執行程式設計執行同步條件變數

轉載請註明出處:http://blog.csdn.net/suool/article/details/38582521. 基本概念與原理 互斥鎖能夠解決資源的互斥訪問,但是在某些情況下,互斥並不能解決問題,比如兩個執行緒需 要互斥的處理各自的操作,但是一個執行緒的操作僅僅存

類似plustoken錢包系統開發幣種錢包開發

自己 模式 閑置 擔心 等等 什麽 出現 自動 聽說 了解數字資產錢包市場的用戶大概都聽說過plustoken,這款多幣種錢包中,除了擁有傳統的能夠多儲存數字資產的功能以外,還帶有智能狗自動搬磚的功能,用戶將數字資產存放在錢包系統裏面,開啟這一功能,在一定時間裏就能夠得到一

day 33 執行學習執行程序效率對比. 鎖. 訊號量 . 事件

一  . 執行緒    執行緒是cpu最小的執行單位,是能獨立執行的基本單位,程序是資源分配的最小單位。且:每個程序中最小有一個執行緒   執行緒與程序的區別:   1)地址空間和其它資源(如開啟檔案):程序間相互獨立,同一程序的各執行緒間共享。某程序內的執行緒在其它程序不可見。

VS C++ 執行執行同步

執行緒同步解決        不同執行緒函式的執行順序,進行執行緒協調。  APIDWORD WINAPI WaitForSingleObject( HANDLE hHandle, // 物件控制代

死磕 java執行系列執行模型

(2)執行緒模型有哪些? (3)各語言使用的是哪種執行緒模型? 簡介 在Java中,我們平時所說的併發程式設計、多執行緒、共享資源等概念都是與執行緒相關的,這裡所說的執行緒實際上應該叫作“使用者執行緒”,而對應到作業系統,還有另外一種執行緒叫作“核心執行緒”。 使用者執行緒位於核心之上,它的管理無需核心支援

死磕 java執行系列執行池深入解析——體系結構

(手機橫屏看原始碼更方便) 注:java原始碼分析部分如無特殊說明均基於 java8 版本。 簡介 Java的執行緒池是塊硬骨頭,對執行緒池的原始碼做深入研究不僅能提高對Java整個併發程式設計的理解,也能提高自己在面試中的表現,增加被錄取的可能性。 本系列將分成很多個章節,本章作為執行緒池的第一章將對

死磕 java執行系列執行池深入解析——生命週期

(手機橫屏看原始碼更方便) 注:java原始碼分析部分如無特殊說明均基於 java8 版本。 注:執行緒池原始碼部分如無特殊說明均指ThreadPoolExecutor類。 簡介 上一章我們一起重溫了下執行緒的生命週期(六種狀態還記得不?),但是你知不知道其實執行緒池也是有生命週期的呢?! 問題 (1)

死磕 java執行系列執行池深入解析——普通任務執行流程

(手機橫屏看原始碼更方便) 注:java原始碼分析部分如無特殊說明均基於 java8 版本。 注:執行緒池原始碼部分如無特殊說明均指ThreadPoolExecutor類。 簡介 前面我們一起學習了Java中執行緒池的體系結構、構造方法和生命週期,本章我們一起來學習執行緒池中普通任務到底是怎麼執行的。

死磕 java執行系列執行池深入解析——未來任務執行流程

(手機橫屏看原始碼更方便) 注:java原始碼分析部分如無特殊說明均基於 java8 版本。 注:執行緒池原始碼部分如無特殊說明均指ThreadPoolExecutor類。 簡介 前面我們一起學習了執行緒池中普通任務的執行流程,但其實執行緒池中還有一種任務,叫作未來任務(future task),使用它