理論基礎
進程與線程
進程
- 進程是一個具有一定獨立功能的程序關于某次數(shù)據(jù)集合的一次運行活動,它是操作系統(tǒng)分配資源的基本單元拉背。
- 進程是指在系統(tǒng)中正在運行的一個應用程序,就是一段程序的執(zhí)行過程,可以理解為手機上一個正在運行的 App网棍。
- 每個進程之間是相互獨立的,每個進程均運行在其專用且受保護的內(nèi)存空間內(nèi)妇智,擁有獨立運行所需的全部資源滥玷。
線程
- 程序執(zhí)行的最小單元氏身,線程是進程中的一個實體。
- 一個進程要想執(zhí)行任務惑畴,必須至少有一個線程蛋欣。應用程序啟動的時候,系統(tǒng)會默認開啟一個線程稱之為主線程(又稱為main線程如贷、UI線程)陷虎。
二者關系
- 線程是進程的執(zhí)行單元,進程的所有任務都在線程中執(zhí)行杠袱。
- 線程是 CPU 分配資源和調(diào)度的最小單位尚猿。
- 一個程序可以對應多個進程(多進程),一個進程中可有多個線程但至少有一個主線程霞掺。
- 同一個進程內(nèi)的線程共享進程的資源谊路。
多線程
- 某個時刻在單個 CPU 的核心只能執(zhí)行一個線程,多線程是指 CPU 快速的在多個線程之間進行切換(調(diào)度)菩彬,形成多個線程同時執(zhí)行的表象〔埃現(xiàn)代 CPU 都是多核,此時可以真正同時處理多個線程骗灶。
- 多線程的目的是為了同時完成多項任務惨恭,通過提高系統(tǒng)的資源利用率來提高系統(tǒng)的效率。
優(yōu)缺點
優(yōu)點
- 提高程序的執(zhí)行效率耙旦。
- 提高資源利用率(CPU脱羡、內(nèi)存利用率)。
缺點
- 開啟線程需要占用一定的內(nèi)存空間免都,如果開啟大量的線程锉罐,會占用大量的內(nèi)存空間,降低程序的性能绕娘。
- 線程越多脓规,CPU 在調(diào)度時開銷就越大。
- 程序設計更加復雜:需要解決線程之間的通信险领、多線程的數(shù)據(jù)共享等問題侨舆。
線程安全
不論線程通過如何調(diào)度或線程如何交替執(zhí)行,在不需要做任何干涉的情況下绢陌,其執(zhí)行結果保持一致符合預期挨下,則稱之為線程安全。
串行脐湾、并行與并發(fā)
- 串行:多個任務臭笆,執(zhí)行完再執(zhí)行另一個。(吃完飯再看電視)
- 并行:每個線程分配給獨立的 CPU 核心,線程同時運行耗啦。(一邊吃飯一邊看電視)
- 并發(fā):多個線程在單個 CPU 核心運行凿菩,同一時間一個線程運行,CPU 通過調(diào)度不斷切換多個線程帜讲,形成多個線程同時執(zhí)行的表象衅谷。(在餐廳吃飯,在客廳看電視)
同步與異步
同步和異步主要區(qū)別:是否開啟新的線程似将。
- 同步執(zhí)行:在當前線程中執(zhí)行任務获黔,不會開啟新線程。
- 異步執(zhí)行:可以在新的線程中執(zhí)行任務在验,可以開啟新的線程玷氏,但不是一定會開啟新的線程。
多線程編程
iOS 中的多線程技術主要分為 3 種腋舌,分別為 Thread盏触、GCD 和 Operation。
Thread
- 面向對象块饺。
- 需要手動創(chuàng)建線程赞辩,但不需要手動銷毀。
方式一
// Target-Action形式
let thread1 = Thread(target: self, selector: #selector(task), object: nil)
// 設置名字
thread1.name = "thread1"
// 啟動
thread1.start()
方式二
// 閉包形式
let thread2 = Thread {
sleep(1)
print(Thread.current)
}
thread2.name = "thread2"
thread2.start()
方式三
// 類方法授艰,也有3種形式辨嗽,以閉包形式為例
// 會直接啟動線程,不需要手動調(diào)用start方法來啟動線程執(zhí)行任務
Thread.detachNewThread {
sleep(1)
print(Thread.current)
}
線程休眠
- sleep():休眠的時間只能為整數(shù)淮腾。
- Thread.sleep(forTimeInterval: ):休眠的時間可以為浮點數(shù)糟需。
GCD
- Grand Central Dispatch(宏大、中央谷朝、調(diào)度)洲押。
- C 語言編寫。
- 充分利用了 CPU 多核特性圆凰,因此效率高杈帐。
- 自動管理線程生命周期。
- 核心概念 — 任務和隊列送朱,將任務放進隊列即可執(zhí)行娘荡。
隊列
隊列類型 | 功能描述 |
---|---|
串行隊列 | 按照任務添加到隊列的順序執(zhí)行干旁,一次只能執(zhí)行一個任務驶沼。 |
并發(fā)隊列 | 同時執(zhí)行一個或多個任務,但任務仍按其添加到隊列的順序啟動争群。 |
主隊列 | 特殊的串行隊列回怜,會在主線程上執(zhí)行任務。 |
DispatchQueue
- 主隊列
// 主隊列
let main = DispatchQueue.main
- 串行隊列
// label:隊列的名稱
// 除label以外的參數(shù)都使用默認值時,返回的是串行隊列玉雾。
let serialQueue = DispatchQueue(label: "serialQueue")
- 并發(fā)隊列
// global并發(fā)隊列
let defaultGlobalDipatchQueue = DispatchQueue.global()
// 帶qos的global并發(fā)隊列
let globalDipatchQueue = DispatchQueue.global(qos: .default)
// 創(chuàng)建一個并發(fā)隊列翔试,參數(shù)attributes需要設置為.concurrent
let concurrentDispatchQueue = DispatchQueue(label: "concurrentQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
sync與async
- sync:同步方法,執(zhí)行時不會立即返回复旬,它會阻塞當前線程垦缅,等待任務執(zhí)行完畢后再執(zhí)行后續(xù)任務。
- async:異步方法驹碍,執(zhí)行時會立即返回然后執(zhí)行后續(xù)任務, 任務會在子線程中執(zhí)行壁涎。
- async 方法有多個參數(shù),其中有 2 個比較重要:
(1)group:關聯(lián)任務的 DispatchGroup志秃。
(2)flags:控制任務執(zhí)行的環(huán)境怔球。(該參數(shù) sync 方法也有)
queue.sync {
// 當前線程執(zhí)行任務
}
queue.async {
// 新線程執(zhí)行任務
}
asyncAfter
在當前隊列中延遲任務的執(zhí)行時間,參數(shù)為DispatchTime
浮还,一般會在當前時間的基礎上加上一個延遲時間(以秒為單位)竟坛。
func dispatchAfter() {
queue.asyncAfter(deadline: DispatchTime.now() + 2) {
print("延遲2s執(zhí)行")
}
// 主隊列延遲執(zhí)行
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
print("主隊列延遲3s執(zhí)行的任務")
}
}
concurrentPerform
- 按指定次數(shù)異步執(zhí)行任務,并且會等待指定次數(shù)的任務全部執(zhí)行完畢才會執(zhí)行后面的任務钧舌,即會阻塞當前線程直到全部任務完成担汤。
- 默認會開啟多少個線程執(zhí)行任務。
func concurrentPerform() {
print("任務開始執(zhí)行")
DispatchQueue.concurrentPerform(iterations: 5) { index in
for i in 0 ... 3 {
Thread.sleep(forTimeInterval: 0.1)
print("這是\(Thread.current)第\(index)次打友恿酢:\(i)")
}
}
print("任務執(zhí)行完畢")
}
barrier
- 用于調(diào)整并發(fā)隊列中任務之間的執(zhí)行順序漫试。
- 同一個隊列中,barrier 之后的任務必須等其執(zhí)行完才會執(zhí)行碘赖。
func barrier() {
let queue = DispatchQueue(label: "queue001", attributes: .concurrent)
queue.async {
sleep(1)
print("\(Thread.current)執(zhí)行任務一")
}
queue.async {
sleep(1)
print("\(Thread.current)執(zhí)行任務二")
}
// 任務四和五會在三之后執(zhí)行
queue.async(flags: .barrier) {
sleep(1)
print("\(Thread.current)執(zhí)行任務三")
}
queue.async {
sleep(1)
print("\(Thread.current)執(zhí)行任務四")
}
queue.async {
sleep(1)
print("\(Thread.current)執(zhí)行任務五")
}
}
DispatchGroup
- 用于需要在多個異步任務完成以后再處理后續(xù)任務的場景驾荣。
- notify:等待 group 中的所有任務執(zhí)行完以后才會執(zhí)行的任務,該操作并不會阻塞當前線程普泡。
- notify 操作可以添加多次播掷,也會執(zhí)行多次纺涤。
func group() {
let group = DispatchGroup()
queue.async(group: group) {
print("網(wǎng)絡請求任務一")
}
queue.async(group: group) {
print("網(wǎng)絡請求任務二")
}
queue.async(group: group) {
print("網(wǎng)絡請求任務三")
}
// 執(zhí)行完前面的任務后回到主線程執(zhí)行后續(xù)任務
group.notify(queue: DispatchQueue.main) {
print("完成任務一拒啰、二、三, 更新UI")
}
queue.async {
print("其他任務四")
}
group.notify(queue: DispatchQueue.main) {
print("完成任務一王带、二砰嘁、三件炉、四, 更新UI")
}
}
- 可以通過
enter()
和leave()
方法顯式表明任務是否執(zhí)行完成,enter()
必須在leave()
之前且二者必須成對出現(xiàn)矮湘。
func group2() {
let group = DispatchGroup()
group.enter()
queue.async(group: group) {
print("網(wǎng)絡請求任務一")
group.leave()
}
group.enter()
queue.async(group: group) {
print("網(wǎng)絡請求任務二")
group.leave()
}
group.enter()
queue.async(group: group) {
print("網(wǎng)絡請求任務三")
group.leave()
}
group.notify(queue: DispatchQueue.main) {
print("完成任務一斟冕、二、三, 更新UI")
}
queue.async {
print("其他任務四")
}
}
DispatchWorkItem
- 對任務的封裝缅阳。
- 單獨使用時需要調(diào)用
perform()
方法執(zhí)行任務磕蛇。
func dispatchWorkItem() {
var value = 10
// 初始化方法傳入一個閉包,閉包中就是需要執(zhí)行的任務
let workItem = DispatchWorkItem {
value += 5
print(Thread.current) // 主線程
}
// 通過perform()方法來喚起DispatchWorkItem執(zhí)行任務
workItem.perform()
print(value)
}
-
隊列中執(zhí)行時不需要手動調(diào)用
perform()
方法。
let workItem = DispatchWorkItem {
for i in 0 ... 10 {
sleep(1)
print(i)
print(Thread.current) // 子線程
}
}
DispatchQueue.global().async(execute: workItem)
- cancel
(1)如果任務已經(jīng)開始執(zhí)行秀撇,即使取消也依然會執(zhí)行超棺。
let workItem = DispatchWorkItem {
for i in 0 ... 10 {
sleep(1)
print(i)
print(Thread.current)
}
}
// 先執(zhí)行
DispatchQueue.global().async(execute: workItem)
// 后取消
workItem.cancel()
// 查看取消狀態(tài)
print(workItem.isCancelled)
(2)如果任務尚未開始執(zhí)行,取消后則不會再執(zhí)行呵燕。
let workItem = DispatchWorkItem {
for i in 0 ... 10 {
sleep(1)
print(i)
print(Thread.current)
}
}
// 先取消
workItem.cancel()
// 再執(zhí)行
DispatchQueue.global().async(execute: workItem)
// 查看取消狀態(tài)
print(workItem.isCancelled)
- wait
(1)無參數(shù):阻塞當前線程直到任務完成棠绘。
let workItem = DispatchWorkItem {
for i in 0 ... 10 {
sleep(1)
print(i)
print(Thread.current)
}
}
DispatchQueue.global().async(execute: workItem)
// 等待
workItem.wait()
// 任務完成后才會執(zhí)行
print("繼續(xù)執(zhí)行任務")
(2)timeout 參數(shù):阻塞當前線程直到 timeout,如果任務完成 timeoutResult 為 success再扭,否則為 timeOut弄唧。
let workItem = DispatchWorkItem {
for i in 0 ... 10 {
sleep(1)
print(i)
print(Thread.current)
}
}
DispatchQueue.global().async(execute: workItem)
// 設置等待時間
let timeoutResult = workItem.wait(timeout: .now() + 3)
// 3秒內(nèi)執(zhí)行完任務則為success,否則timeOut
switch timeoutResult {
case .success:
print("success")
case .timedOut:
print("timedOut")
}
// 3秒以后執(zhí)行
print("繼續(xù)執(zhí)行任務")
- notify:任務完成后需要執(zhí)行的操作霍衫。
let workItem = DispatchWorkItem {
for i in 0 ... 10 {
sleep(1)
print(i)
print(Thread.current)
}
}
DispatchQueue.global().async(execute: workItem)
// 任務完成以后回到指定隊列執(zhí)行任務
workItem.notify(queue: DispatchQueue.main) {
print("任務完成")
}
print("繼續(xù)執(zhí)行任務")
Operation
- 基于 GCD 的封裝候引,更加面向對象,功能相對 GCD 也更加豐富敦跌。
- 核心依然是任務和隊列澄干。
OperationQueue
- 方式一
func operationUseOne() {
// 創(chuàng)建OperationQueue
let operationQueue = OperationQueue()
// 添加Operation
operationQueue.addOperation {
sleep(1)
print("\(Thread.current)執(zhí)行任務一")
}
operationQueue.addOperation {
sleep(1)
print("\(Thread.current)執(zhí)行任務二")
}
operationQueue.addOperation {
sleep(1)
print("\(Thread.current)執(zhí)行任務三")
}
}
- 方式二
func operationUseTwo() {
let operationQueue = OperationQueue()
// BlockOperation
let operation1 = BlockOperation {
print("\(Thread.current)執(zhí)行任務一")
sleep(1)
}
let operation2 = BlockOperation {
print("\(Thread.current)執(zhí)行任務二")
sleep(1)
}
let operation3 = BlockOperation {
print("\(Thread.current)執(zhí)行任務三")
sleep(1)
}
// 逐個添加到OperationQueue
// operationQueue.addOperation(operation1)
// operationQueue.addOperation(operation2)
// operationQueue.addOperation(operation3)
// 一次性添加到OperationQueue
operationQueue.addOperations([operation1, operation2, operation3], waitUntilFinished: false)
// waitUntilFinished
// 如果為false,不會等任務完成再執(zhí)行后續(xù)任務
// 如果為true柠傍,阻塞當前線程麸俘,等待任務完成后再執(zhí)行后續(xù)任務
print("\(Thread.current)執(zhí)行其他任務")
}
- 主隊列
let mainQueue = OperationQueue.main
// 在沒有指定任何隊列的情況下調(diào)用start方法啟動的BlockOperation默認會在主線程執(zhí)行任務
let op = BlockOperation {
sleep(1)
print("\(Thread.current)執(zhí)行任務一")
}
op.start()
maxConcurrentOperationCount
設置 OperationQueue 的最大并發(fā)數(shù),表示的是能同時執(zhí)行的 Operation 的最大數(shù)量惧笛,而不是開啟線程的最大數(shù)量从媚。
func setOperationQueue() {
// 并發(fā)數(shù)
operationQueue.maxConcurrentOperationCount = 2
}
queuePriority
- 設置 Operation 的優(yōu)先級。
- 在同一個隊列中等待調(diào)度的所有 Operation患整,會按照優(yōu)先級排序執(zhí)行拜效,但實際執(zhí)行的順序還是依賴 CPU 的調(diào)度。
func setOperation(op:Operation){
// 優(yōu)先級
op.queuePriority = .high
}
addDependency與completionBlock
- addDependency 用于設置 Operation 之間的依賴關系各谚。
- 依賴操作必須在 Operation 添加到隊列之前進行紧憾。
- 可以跨隊列進行依賴操作。
- completionBlock 用于設置 Operation 完成時的回調(diào)昌渤。
func dependency() {
let operationQueue = OperationQueue()
let operation1 = BlockOperation {
print("\(Thread.current)執(zhí)行任務一")
sleep(1)
}
// 監(jiān)聽Operation完成
operation1.completionBlock = {
print("\(Thread.current)完成任務一")
}
let operation2 = BlockOperation {
print("\(Thread.current)執(zhí)行任務二")
sleep(1)
}
operation2.completionBlock = {
print("\(Thread.current)完成任務二")
}
// 添加依賴
// operation2在operation1執(zhí)行完再執(zhí)行(并不是等completionBlock執(zhí)行完再執(zhí)行赴穗,而是BlockOperation體執(zhí)行完就開始執(zhí)行)
operation2.addDependency(operation1)
let operation3 = BlockOperation {
print("\(Thread.current)執(zhí)行任務三")
sleep(1)
}
operation3.completionBlock = {
print("\(Thread.current)完成任務三")
}
// operation3在operation2執(zhí)行完再執(zhí)行
operation3.addDependency(operation2)
operationQueue.addOperations([operation1, operation2, operation3], waitUntilFinished: false)
print("\(Thread.current)執(zhí)行其他任務")
}
barrier
類似 GCD 的 barrier。
func barrier() {
let operationQueue = OperationQueue()
operationQueue.addOperation {
sleep(1)
print("\(Thread.current)執(zhí)行任務一")
}
operationQueue.addOperation {
sleep(1)
print("\(Thread.current)執(zhí)行任務二")
}
// 任務四和五會在三之后執(zhí)行
operationQueue.addBarrierBlock {
sleep(1)
print("\(Thread.current)執(zhí)行任務三")
}
operationQueue.addOperation {
sleep(1)
print("\(Thread.current)執(zhí)行任務四")
}
operationQueue.addOperation {
sleep(1)
print("\(Thread.current)執(zhí)行任務五")
}
}
suspend膀息、resume與cancel
- suspend:掛起般眉,OperationQueue 中還沒有被 CPU 調(diào)度的 Operation 才會被掛起,那些已經(jīng)被 CPU 調(diào)度的 Operation 不會被掛起潜支。
func suspend() {
if operationQueue.operationCount != 0 && operationQueue.isSuspended == false {
operationQueue.isSuspended = true
}
}
- resume:重啟甸赃,OperationQueue 中被掛起的 Operation 可以繼續(xù)執(zhí)行。
func resume() {
if operationQueue.operationCount != 0 && operationQueue.isSuspended == true {
operationQueue.isSuspended = false
}
}
- cancel:取消毁腿,還沒有被 CPU 調(diào)度的 Operation 才會被取消辑奈,但無法讓其再次運行。分為 2 種:
(1)取消單個已烤。
(2)取消所有鸠窗。
func cancel() {
// Operation 取消
operation.cancel()
// OperationQueue 取消所有
operationQueue.cancelAllOperations()
}
安全性問題
多線程編程中,應該盡量避免資源在線程之間共享胯究,以減少線程間的相互影響稍计。有兩個重要的概念:
- 臨界資源:一次只能允許一個線程使用的共享資源。
- 臨界區(qū):訪問臨界資源的那段代碼裕循。
在實際開發(fā)中臣嚣,經(jīng)常存在多個線程訪問同一個共享資源的情況,那么如何保證多線程執(zhí)行結果的正確性剥哑?在 iOS 中主要提供了 2 種技術 — 鎖和信號量硅则。
鎖
- 互斥鎖:保證在任何時候,都只有一個線程訪問對象株婴。當獲取鎖失敗時怎虫,線程會進入睡眠,等待鎖釋放時被喚醒困介。
- 遞歸鎖:特殊的互斥鎖大审。它的特點是同一個線程可以加鎖 N 次而不會引發(fā)死鎖。
- 自旋鎖 :它不會引起調(diào)用者睡眠座哩,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持徒扶,調(diào)用者就一直循環(huán)嘗試,直到該自旋鎖的保持者已經(jīng)釋放了鎖根穷;因為不會引起調(diào)用者睡眠姜骡,所以效率高于互斥鎖。 缺點:
(1)調(diào)用者在未獲得鎖的情況下會一直運行屿良,如果不能在很短的時間內(nèi)獲得鎖溶浴,會使CPU效率降低。所以自旋鎖就適用于臨界區(qū)持鎖時間非常短且CPU資源不緊張的場景管引。
(2)在用自旋鎖時(如遞歸調(diào)用)有可能造成死鎖士败。
pthread
- 比較底層,現(xiàn)在使用較少褥伴。
var mutex: pthread_mutex_t = {
// 初始化鎖屬性
var mutexattr = pthread_mutexattr_t()
// 鎖屬性賦值
pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_DEFAULT)
// 初始化鎖
var mutex = pthread_mutex_t()
// pthread_mutex_init(&mutex, nil)
// mutexattr傳nil表示default
pthread_mutex_init(&mutex, &mutexattr)
// 使用鎖屬性之后要釋放
pthread_mutexattr_destroy(&mutexattr)
// 返回鎖
return mutex
}()
// 線程業(yè)務代碼
DispatchQueue.global().async {
// 加鎖
pthread_mutex_lock(&mutex)
// 臨界區(qū)
// 解鎖
pthread_mutex_unlock(&mutex)
}
- 銷毀鎖谅将。
deinit {
// 銷毀鎖
pthread_mutex_destroy(&mutex)
}
NS系列鎖
包括NSLock、NSCondition重慢、NSConditionLock饥臂、NSRecursiveLock
,都遵守了NSLocking
協(xié)議似踱,隅熙。
- NSLocking協(xié)議稽煤。
public protocol NSLocking {
func lock() // 加鎖
func unlock() // 解鎖
}
- NSLock:互斥鎖。
// 初始化
let lock = NSLock()
// 加鎖
lock.lock()
// 臨界區(qū)
// 解鎖
lock.unlock()
- NSCondition:常用于生產(chǎn)者消費者模式囚戚。
// 初始化
let lock = NSCondition()
var products = [Int]()
// 消費者
func consume() {
DispatchQueue.global().async {
// 加鎖
lock.lock()
// 沒有商品掛起線程
while products.count == 0 {
lock.wait()
}
// 消費產(chǎn)品
let product = products.remove(at: 0)
print("消費產(chǎn)品\(product)")
// 解鎖
lock.unlock()
}
}
// 生產(chǎn)者
func produce() {
DispatchQueue.global().async {
// 加鎖
lock.lock()
// 生產(chǎn)產(chǎn)品
let product = Int.random(in: 0 ... 100)
products.append(product)
print("生產(chǎn)產(chǎn)品\(product)")
// 喚醒消費者
lock.signal()
// 解鎖
lock.unlock()
}
}
while true {
consume()
sleep(1)
produce()
}
- NSConditionLock:條件鎖酵熙,對 NSCondition 的進一步封裝。
// 初始化時condition為0
let lock = NSConditionLock(condition: 0)
var products = [Int]()
// 消費者
func consume() {
DispatchQueue.global().async {
// 加鎖驰坊,當參數(shù)與初始化時condition不一致時進行等待
lock.lock(whenCondition: 1)
// 消費產(chǎn)品
let product = products.remove(at: 0)
print("消費產(chǎn)品\(product)")
// 解鎖匾二,修改condition的值為0
lock.unlock(withCondition: 0)
}
}
// 生產(chǎn)者
func produce() {
DispatchQueue.global().async {
// 加鎖,與初始化時condition一致拳芙,繼續(xù)執(zhí)行
lock.lock(whenCondition: 0)
// 生產(chǎn)產(chǎn)品
let product = Int.random(in: 0 ... 100)
products.append(product)
print("生產(chǎn)產(chǎn)品\(product)")
// 解鎖察藐,修改condition的值為1
lock.unlock(withCondition: 1)
}
}
while true {
consume()
sleep(1)
produce()
}
- NSRecursiveLock:遞歸鎖。
// 初始化
let lock = NSRecursiveLock()
var count = 5
func recursive(value: Int) {
// 加鎖(換成其他的鎖會死鎖)
lock.lock()
// 大于0才繼續(xù)后面的操作
guard value > 0 else {
return
}
// 打印
print(value)
// 休眠
sleep(1)
// 遞歸次數(shù)減1
count -= 1
// 遞歸調(diào)用
recursive(value: count)
// 解鎖
lock.unlock()
}
DispatchQueue.global().async {
print("開始")
recursive(value: count)
print("結束")
}
objc_sync
let lock: Int = 0
// 加鎖
objc_sync_enter(lock) // 很多時候參數(shù)為self
// 臨界區(qū)
// 解鎖
objc_sync_exit(lock)
OSSpinLock自旋鎖
由于存在因為低優(yōu)先級爭奪資源導致死鎖的問題舟扎,所以在 iOS 10 之后已廢棄分飞,替換它的是 os_unfair_lock。
os_unfair_lock
一種互斥鎖睹限,內(nèi)置于os
模塊浸须。
var lock = os_unfair_lock()
// 加鎖
os_unfair_lock_lock(&lock)
// 臨界區(qū)
// 解鎖
os_unfair_lock_unlock(&lock)
信號量
DispatchSemaphore 是一種基于計數(shù)的信號量。它可以設定一個閥值邦泄,多個線程競爭獲取許可信號删窒,超過閥值后,線程申請許可信號將會被阻塞顺囊。主要用于線程之間的數(shù)據(jù)同步肌索。
- DispatchSemaphore(value: ):創(chuàng)建信號量,value 為初始值特碳。
- wait:根據(jù)當前信號量的值進行判斷:
(1)若大于 0诚亚,則將信號量減 1 ,繼續(xù)執(zhí)行后續(xù)任務午乓。
(2)若小于等于 0站宗,則阻塞當前線程,直到信號量大于 0 或者經(jīng)過一個閾值時間才會執(zhí)行后續(xù)任務益愈。 - signal:信號量加 1梢灭。
DispatchSemaphore
// 創(chuàng)建信號量,初始值為0
let semaphore = DispatchSemaphore(value: 0)
// 線程業(yè)務代碼
DispatchQueue.global().async {
// 臨界區(qū)
semaphore.signal()
}
semaphore.wait(timeout: .distantFuture)
UI更新問題
- 當 App 運行以后蒸其,主線程隨之啟動敏释。該線程需要接收用戶的交互,完成界面的更新等操作摸袁,因此必須保證它的流暢性钥顽,耗時的操作不能放在主線程中執(zhí)行,否則會造成界面的卡頓甚至崩潰靠汁。
- iOS 規(guī)定不能在子線程中更新 UI 界面蜂大,更新 UI 的操作必須在主線程中進行闽铐。如果在子線程中更新了 UI,程序在編譯時并不會報錯奶浦,但運行時會出現(xiàn)意料不到的結果甚至崩潰兄墅,此時控制臺和 Xcode 也會有相應的錯誤信息輸出和提示。
- 針對 3 種不同的線程實現(xiàn)方式财喳,回到主線程也有 3 種方式。
import UIKit
// MARK:- Thread模式
func threadMode(){
let thread = Thread {
print("\(Thread.current)執(zhí)行任務")
// 休眠
sleep(3)
// 更新UI
self.perform(#selector(self.updateUI), on: Thread.main, with: nil, waitUntilDone: false)
}
thread.start()
}
@objc func updateUI() {
self.infoLb.text = "Thread方式更新UI"
}
// MARK:- GCD模式
func gcdMode(){
DispatchQueue.global().async {
print("\(Thread.current)執(zhí)行任務")
// 休眠
sleep(3)
// 更新UI
DispatchQueue.main.async {
self.infoLb.text = "GCD方式更新UI"
}
}
}
// MARK:- Operation模式
func operationMode(){
OperationQueue().addOperation {
print("\(Thread.current)執(zhí)行任務")
// 休眠
sleep(3)
// 更新UI
OperationQueue.main.addOperation {
self.infoLb.text = "Operation方式更新UI"
}
}
}