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。如果執行緒B把lock和unlock方法去掉之後,則執行緒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),使用它