iOS14開發(fā)-多線程

理論基礎(chǔ)

進(jìn)程與線程

進(jìn)程

  • 進(jìn)程是一個(gè)具有一定獨(dú)立功能的程序關(guān)于某次數(shù)據(jù)集合的一次運(yùn)行活動(dòng),它是操作系統(tǒng)分配資源的基本單元。
  • 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,就是一段程序的執(zhí)行過程,可以理解為手機(jī)上一個(gè)正在運(yùn)行的 App。
  • 每個(gè)進(jìn)程之間是相互獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi),擁有獨(dú)立運(yùn)行所需的全部資源。

線程

  • 程序執(zhí)行的最小單元,線程是進(jìn)程中的一個(gè)實(shí)體。
  • 一個(gè)進(jìn)程要想執(zhí)行任務(wù),必須至少有一個(gè)線程。應(yīng)用程序啟動(dòng)的時(shí)候,系統(tǒng)會(huì)默認(rèn)開啟一個(gè)線程稱之為主線程(又稱為main線程、UI線程)

二者關(guān)系

  • 線程是進(jìn)程的執(zhí)行單元,進(jìn)程的所有任務(wù)都在線程中執(zhí)行。
  • 線程是 CPU 分配資源和調(diào)度的最小單位。
  • 一個(gè)程序可以對(duì)應(yīng)多個(gè)進(jìn)程(多進(jìn)程),一個(gè)進(jìn)程中可有多個(gè)線程但至少有一個(gè)主線程。
  • 同一個(gè)進(jìn)程內(nèi)的線程共享進(jìn)程的資源。

多線程

  • 某個(gè)時(shí)刻在單個(gè) CPU 的核心只能執(zhí)行一個(gè)線程,多線程是指 CPU 快速的在多個(gè)線程之間進(jìn)行切換(調(diào)度),形成多個(gè)線程同時(shí)執(zhí)行的表象。現(xiàn)代 CPU 都是多核,此時(shí)可以真正同時(shí)處理多個(gè)線程。
  • 多線程的目的是為了同時(shí)完成多項(xiàng)任務(wù),通過提高系統(tǒng)的資源利用率來提高系統(tǒng)的效率。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 提高程序的執(zhí)行效率。
  • 提高資源利用率(CPU、內(nèi)存利用率)。

缺點(diǎn)

  • 開啟線程需要占用一定的內(nèi)存空間,如果開啟大量的線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能。
  • 線程越多,CPU 在調(diào)度時(shí)開銷就越大。
  • 程序設(shè)計(jì)更加復(fù)雜:需要解決線程之間的通信、多線程的數(shù)據(jù)共享等問題。

線程安全

不論線程通過如何調(diào)度或線程如何交替執(zhí)行,在不需要做任何干涉的情況下,其執(zhí)行結(jié)果保持一致符合預(yù)期,則稱之為線程安全。

串行、并行與并發(fā)

  • 串行:多個(gè)任務(wù),執(zhí)行完再執(zhí)行另一個(gè)。(吃完飯?jiān)倏措娨暎?/li>
  • 并行:每個(gè)線程分配給獨(dú)立的 CPU 核心,線程同時(shí)運(yùn)行。(一邊吃飯一邊看電視)
  • 并發(fā):多個(gè)線程在單個(gè) CPU 核心運(yùn)行,同一時(shí)間一個(gè)線程運(yùn)行,CPU 通過調(diào)度不斷切換多個(gè)線程,形成多個(gè)線程同時(shí)執(zhí)行的表象。(在餐廳吃飯,在客廳看電視)

同步與異步

同步和異步主要區(qū)別:是否開啟新的線程。

  • 同步執(zhí)行:在當(dāng)前線程中執(zhí)行任務(wù),不會(huì)開啟新線程。
  • 異步執(zhí)行:可以在新的線程中執(zhí)行任務(wù),可以開啟新的線程,但不是一定會(huì)開啟新的線程。

多線程編程

iOS 中的多線程技術(shù)主要分為 3 種,分別為 Thread、GCD 和 Operation。

Thread

  • 面向?qū)ο蟆?/li>
  • 需要手動(dòng)創(chuàng)建線程,但不需要手動(dòng)銷毀。

方式一

// Target-Action形式
let thread1 = Thread(target: self, selector: #selector(task), object: nil)
// 設(shè)置名字
thread1.name = "thread1"       
// 啟動(dòng)
thread1.start()       

方式二

// 閉包形式
let thread2 = Thread {
    sleep(1)

    print(Thread.current)
}

thread2.name = "thread2"
thread2.start()

方式三

// 類方法,也有3種形式,以閉包形式為例
// 會(huì)直接啟動(dòng)線程,不需要手動(dòng)調(diào)用start方法來啟動(dòng)線程執(zhí)行任務(wù)
Thread.detachNewThread {    
    sleep(1)
    
    print(Thread.current)  
}

線程休眠

  • sleep():休眠的時(shí)間只能為整數(shù)。
  • Thread.sleep(forTimeInterval: ):休眠的時(shí)間可以為浮點(diǎn)數(shù)。

GCD

  • Grand Central Dispatch(宏大、中央、調(diào)度)。
  • C 語言編寫。
  • 充分利用了 CPU 多核特性,因此效率高。
  • 自動(dòng)管理線程生命周期。
  • 核心概念 — 任務(wù)和隊(duì)列,將任務(wù)放進(jìn)隊(duì)列即可執(zhí)行。

隊(duì)列

隊(duì)列類型 功能描述
串行隊(duì)列 按照任務(wù)添加到隊(duì)列的順序執(zhí)行,一次只能執(zhí)行一個(gè)任務(wù)。
并發(fā)隊(duì)列 同時(shí)執(zhí)行一個(gè)或多個(gè)任務(wù),但任務(wù)仍按其添加到隊(duì)列的順序啟動(dòng)。
主隊(duì)列 特殊的串行隊(duì)列,會(huì)在主線程上執(zhí)行任務(wù)。

DispatchQueue

  • 主隊(duì)列
// 主隊(duì)列
let main = DispatchQueue.main  
  • 串行隊(duì)列
// label:隊(duì)列的名稱
// 除label以外的參數(shù)都使用默認(rèn)值時(shí),返回的是串行隊(duì)列。
let serialQueue = DispatchQueue(label: "serialQueue")   
  • 并發(fā)隊(duì)列
// global并發(fā)隊(duì)列
let defaultGlobalDipatchQueue =  DispatchQueue.global()

// 帶qos的global并發(fā)隊(duì)列
let globalDipatchQueue = DispatchQueue.global(qos: .default)

// 創(chuàng)建一個(gè)并發(fā)隊(duì)列,參數(shù)attributes需要設(shè)置為.concurrent
let concurrentDispatchQueue = DispatchQueue(label: "concurrentQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)

sync與async

  • sync同步方法,執(zhí)行時(shí)不會(huì)立即返回,它會(huì)阻塞當(dāng)前線程,等待任務(wù)執(zhí)行完畢后再執(zhí)行后續(xù)任務(wù)。
  • async異步方法,執(zhí)行時(shí)會(huì)立即返回然后執(zhí)行后續(xù)任務(wù), 任務(wù)會(huì)在子線程中執(zhí)行。
  • async 方法有多個(gè)參數(shù),其中有 2 個(gè)比較重要:
    (1)group:關(guān)聯(lián)任務(wù)的 DispatchGroup。
    (2)flags:控制任務(wù)執(zhí)行的環(huán)境。(該參數(shù) sync 方法也有)
queue.sync {
    // 當(dāng)前線程執(zhí)行任務(wù)
}

queue.async {
    // 新線程執(zhí)行任務(wù)
}

asyncAfter

在當(dāng)前隊(duì)列中延遲任務(wù)的執(zhí)行時(shí)間,參數(shù)為DispatchTime,一般會(huì)在當(dāng)前時(shí)間的基礎(chǔ)上加上一個(gè)延遲時(shí)間(以秒為單位)。

func dispatchAfter() { 
    queue.asyncAfter(deadline: DispatchTime.now() + 2) {
        print("延遲2s執(zhí)行")  
    }
    
    // 主隊(duì)列延遲執(zhí)行
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) {
        print("主隊(duì)列延遲3s執(zhí)行的任務(wù)")
    }  
}

concurrentPerform

  • 按指定次數(shù)異步執(zhí)行任務(wù),并且會(huì)等待指定次數(shù)的任務(wù)全部執(zhí)行完畢才會(huì)執(zhí)行后面的任務(wù),即會(huì)阻塞當(dāng)前線程直到全部任務(wù)完成。
  • 默認(rèn)會(huì)開啟多少個(gè)線程執(zhí)行任務(wù)。
func concurrentPerform() {
    print("任務(wù)開始執(zhí)行")

    DispatchQueue.concurrentPerform(iterations: 5) { index in
        for i in 0 ... 3 {
            Thread.sleep(forTimeInterval: 0.1)
            print("這是\(Thread.current)第\(index)次打印:\(i)")
        }
    }

    print("任務(wù)執(zhí)行完畢")
}

barrier

  • 用于調(diào)整并發(fā)隊(duì)列中任務(wù)之間的執(zhí)行順序。
  • 同一個(gè)隊(duì)列中,barrier 之后的任務(wù)必須等其執(zhí)行完才會(huì)執(zhí)行。
func barrier() {
    let queue = DispatchQueue(label: "queue001", attributes: .concurrent)

    queue.async {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)一")
    }

    queue.async {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)二")
    }

    // 任務(wù)四和五會(huì)在三之后執(zhí)行
    queue.async(flags: .barrier) {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)三")
    }

    queue.async {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)四")
    }

    queue.async {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)五")
    }
}

DispatchGroup

  • 用于需要在多個(gè)異步任務(wù)完成以后再處理后續(xù)任務(wù)的場(chǎng)景。
  • notify:等待 group 中的所有任務(wù)執(zhí)行完以后才會(huì)執(zhí)行的任務(wù),該操作并不會(huì)阻塞當(dāng)前線程。
  • notify 操作可以添加多次,也會(huì)執(zhí)行多次。
func group() {
    let group = DispatchGroup()

    queue.async(group: group) {
        print("網(wǎng)絡(luò)請(qǐng)求任務(wù)一")
    }

    queue.async(group: group) {
        print("網(wǎng)絡(luò)請(qǐng)求任務(wù)二")
    }

    queue.async(group: group) {
        print("網(wǎng)絡(luò)請(qǐng)求任務(wù)三")
    }

    // 執(zhí)行完前面的任務(wù)后回到主線程執(zhí)行后續(xù)任務(wù)
    group.notify(queue: DispatchQueue.main) {
        print("完成任務(wù)一、二、三, 更新UI")
    }

    queue.async {
        print("其他任務(wù)四")
    }
    
    group.notify(queue: DispatchQueue.main) {
        print("完成任務(wù)一、二、三、四, 更新UI")
    }
}
  • 可以通過enter()leave()方法顯式表明任務(wù)是否執(zhí)行完成,enter()必須在leave()之前且二者必須成對(duì)出現(xiàn)。
func group2() {
    let group = DispatchGroup()

    group.enter()
    queue.async(group: group) {
        print("網(wǎng)絡(luò)請(qǐng)求任務(wù)一")
        group.leave()
    }

    group.enter()
    queue.async(group: group) {
        print("網(wǎng)絡(luò)請(qǐng)求任務(wù)二")
        group.leave()
    }

    group.enter()
    queue.async(group: group) {
        print("網(wǎng)絡(luò)請(qǐng)求任務(wù)三")
        group.leave()
    }

    group.notify(queue: DispatchQueue.main) {
        print("完成任務(wù)一、二、三, 更新UI")
    }

    queue.async {
        print("其他任務(wù)四")
    }
}

DispatchWorkItem

  • 對(duì)任務(wù)的封裝。
  • 單獨(dú)使用時(shí)需要調(diào)用perform()方法執(zhí)行任務(wù)。
func dispatchWorkItem() {    
    var value = 10
    // 初始化方法傳入一個(gè)閉包,閉包中就是需要執(zhí)行的任務(wù)
    let workItem = DispatchWorkItem {
        value += 5
        print(Thread.current) // 主線程
    }
    
    // 通過perform()方法來喚起DispatchWorkItem執(zhí)行任務(wù)
    workItem.perform()
    
    print(value)
}
  • 隊(duì)列中執(zhí)行時(shí)不需要手動(dòng)調(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)如果任務(wù)已經(jīng)開始執(zhí)行,即使取消也依然會(huì)執(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)如果任務(wù)尚未開始執(zhí)行,取消后則不會(huì)再執(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ù):阻塞當(dāng)前線程直到任務(wù)完成。

let workItem = DispatchWorkItem {
    for i in 0 ... 10 {
        sleep(1)
        print(i)
        print(Thread.current)
    }
}

DispatchQueue.global().async(execute: workItem)
// 等待
workItem.wait()
// 任務(wù)完成后才會(huì)執(zhí)行
print("繼續(xù)執(zhí)行任務(wù)")

(2)timeout 參數(shù):阻塞當(dāng)前線程直到 timeout,如果任務(wù)完成 timeoutResult 為 success,否則為 timeOut。

let workItem = DispatchWorkItem {
    for i in 0 ... 10 {
        sleep(1)
        print(i)
        print(Thread.current)
    }
}

DispatchQueue.global().async(execute: workItem)
// 設(shè)置等待時(shí)間
let timeoutResult = workItem.wait(timeout: .now() + 3)
// 3秒內(nèi)執(zhí)行完任務(wù)則為success,否則timeOut
switch timeoutResult {
case .success:
    print("success")
case .timedOut:
    print("timedOut")
}

// 3秒以后執(zhí)行
print("繼續(xù)執(zhí)行任務(wù)")
  • notify:任務(wù)完成后需要執(zhí)行的操作。
let workItem = DispatchWorkItem {
    for i in 0 ... 10 {
        sleep(1)
        print(i)
        print(Thread.current)
    }
}

DispatchQueue.global().async(execute: workItem)
// 任務(wù)完成以后回到指定隊(duì)列執(zhí)行任務(wù)
workItem.notify(queue: DispatchQueue.main) {
    print("任務(wù)完成")
}

print("繼續(xù)執(zhí)行任務(wù)")

Operation

  • 基于 GCD 的封裝,更加面向?qū)ο?,功能相?duì) GCD 也更加豐富。
  • 核心依然是任務(wù)和隊(duì)列。

OperationQueue

  • 方式一
func operationUseOne() {
    // 創(chuàng)建OperationQueue
    let operationQueue = OperationQueue()
    
    // 添加Operation
    operationQueue.addOperation {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)一")
    }

    operationQueue.addOperation {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)二")
    }

    operationQueue.addOperation {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)三")
    }
}
  • 方式二
func operationUseTwo() {
    let operationQueue = OperationQueue()

    // BlockOperation
    let operation1 = BlockOperation {
        print("\(Thread.current)執(zhí)行任務(wù)一")
        sleep(1)
    }

    let operation2 = BlockOperation {
        print("\(Thread.current)執(zhí)行任務(wù)二")
        sleep(1)
    }

    let operation3 = BlockOperation {
        print("\(Thread.current)執(zhí)行任務(wù)三")
        sleep(1)
    }

    // 逐個(gè)添加到OperationQueue
    // operationQueue.addOperation(operation1)
    // operationQueue.addOperation(operation2)
    // operationQueue.addOperation(operation3)

    // 一次性添加到OperationQueue
    operationQueue.addOperations([operation1, operation2, operation3], waitUntilFinished: false)

    // waitUntilFinished
    // 如果為false,不會(huì)等任務(wù)完成再執(zhí)行后續(xù)任務(wù)
    // 如果為true,阻塞當(dāng)前線程,等待任務(wù)完成后再執(zhí)行后續(xù)任務(wù)
    print("\(Thread.current)執(zhí)行其他任務(wù)")
}
  • 主隊(duì)列
let mainQueue = OperationQueue.main

// 在沒有指定任何隊(duì)列的情況下調(diào)用start方法啟動(dòng)的BlockOperation默認(rèn)會(huì)在主線程執(zhí)行任務(wù)
let op = BlockOperation {
    sleep(1)
    print("\(Thread.current)執(zhí)行任務(wù)一")
}

op.start()

maxConcurrentOperationCount

設(shè)置 OperationQueue 的最大并發(fā)數(shù),表示的是能同時(shí)執(zhí)行的 Operation 的最大數(shù)量,而不是開啟線程的最大數(shù)量。

func setOperationQueue() {
      // 并發(fā)數(shù)
     operationQueue.maxConcurrentOperationCount = 2
}

queuePriority

  • 設(shè)置 Operation 的優(yōu)先級(jí)。
  • 在同一個(gè)隊(duì)列中等待調(diào)度的所有 Operation,會(huì)按照優(yōu)先級(jí)排序執(zhí)行,但實(shí)際執(zhí)行的順序還是依賴 CPU 的調(diào)度。
func setOperation(op:Operation){
    // 優(yōu)先級(jí)
    op.queuePriority = .high
}

addDependency與completionBlock

  • addDependency 用于設(shè)置 Operation 之間的依賴關(guān)系。
  • 依賴操作必須在 Operation 添加到隊(duì)列之前進(jìn)行。
  • 可以跨隊(duì)列進(jìn)行依賴操作。
  • completionBlock 用于設(shè)置 Operation 完成時(shí)的回調(diào)。
func dependency() {
    let operationQueue = OperationQueue()

    let operation1 = BlockOperation {
        print("\(Thread.current)執(zhí)行任務(wù)一")
        sleep(1)
    }

    // 監(jiān)聽Operation完成
    operation1.completionBlock = {
        print("\(Thread.current)完成任務(wù)一")
    }

    let operation2 = BlockOperation {
        print("\(Thread.current)執(zhí)行任務(wù)二")
        sleep(1)
    }

    operation2.completionBlock = {
        print("\(Thread.current)完成任務(wù)二")
    }

    // 添加依賴
    // operation2在operation1執(zhí)行完再執(zhí)行(并不是等completionBlock執(zhí)行完再執(zhí)行,而是BlockOperation體執(zhí)行完就開始執(zhí)行)
    operation2.addDependency(operation1)

    let operation3 = BlockOperation {
        print("\(Thread.current)執(zhí)行任務(wù)三")
        sleep(1)
    }

    operation3.completionBlock = {
        print("\(Thread.current)完成任務(wù)三")
    }

    // operation3在operation2執(zhí)行完再執(zhí)行
    operation3.addDependency(operation2)

    operationQueue.addOperations([operation1, operation2, operation3], waitUntilFinished: false)

    print("\(Thread.current)執(zhí)行其他任務(wù)")
}

barrier

類似 GCD 的 barrier。

func barrier() {
    let operationQueue = OperationQueue()

    operationQueue.addOperation {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)一")
    }

    operationQueue.addOperation {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)二")
    }

    // 任務(wù)四和五會(huì)在三之后執(zhí)行
    operationQueue.addBarrierBlock {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)三")
    }

    operationQueue.addOperation {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)四")
    }

    operationQueue.addOperation {
        sleep(1)
        print("\(Thread.current)執(zhí)行任務(wù)五")
    }
}

suspend、resume與cancel

  • suspend:掛起,OperationQueue 中還沒有被 CPU 調(diào)度的 Operation 才會(huì)被掛起,那些已經(jīng)被 CPU 調(diào)度的 Operation 不會(huì)被掛起。
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 才會(huì)被取消,但無法讓其再次運(yùn)行。分為 2 種:
    (1)取消單個(gè)。
    (2)取消所有。
func cancel() {
    //  Operation 取消 
    operation.cancel()
    // OperationQueue 取消所有
    operationQueue.cancelAllOperations()
}

安全性問題

多線程編程中,應(yīng)該盡量避免資源在線程之間共享,以減少線程間的相互影響。有兩個(gè)重要的概念:

  1. 臨界資源:一次只能允許一個(gè)線程使用的共享資源。
  2. 臨界區(qū):訪問臨界資源的那段代碼。

在實(shí)際開發(fā)中,經(jīng)常存在多個(gè)線程訪問同一個(gè)共享資源的情況,那么如何保證多線程執(zhí)行結(jié)果的正確性?在 iOS 中主要提供了 2 種技術(shù) — 鎖和信號(hào)量

  • 互斥鎖:保證在任何時(shí)候,都只有一個(gè)線程訪問對(duì)象。當(dāng)獲取鎖失敗時(shí),線程會(huì)進(jìn)入睡眠,等待鎖釋放時(shí)被喚醒。
  • 遞歸鎖:特殊的互斥鎖。它的特點(diǎn)是同一個(gè)線程可以加鎖 N 次而不會(huì)引發(fā)死鎖。
  • 自旋鎖 :它不會(huì)引起調(diào)用者睡眠,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)嘗試,直到該自旋鎖的保持者已經(jīng)釋放了鎖;因?yàn)椴粫?huì)引起調(diào)用者睡眠,所以效率高于互斥鎖。 缺點(diǎn):
    (1)調(diào)用者在未獲得鎖的情況下會(huì)一直運(yùn)行,如果不能在很短的時(shí)間內(nèi)獲得鎖,會(huì)使CPU效率降低。所以自旋鎖就適用于臨界區(qū)持鎖時(shí)間非常短且CPU資源不緊張的場(chǎng)景。
    (2)在用自旋鎖時(shí)(如遞歸調(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è)務(wù)代碼
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)者消費(fèi)者模式。
// 初始化
let lock = NSCondition()
var products = [Int]()

// 消費(fèi)者
func consume() {
    DispatchQueue.global().async {
        // 加鎖
        lock.lock()
        // 沒有商品掛起線程
        while products.count == 0 {
            lock.wait()
        }
        // 消費(fèi)產(chǎn)品
        let product = products.remove(at: 0)
        print("消費(fèi)產(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)")
        // 喚醒消費(fèi)者
        lock.signal()
        // 解鎖
        lock.unlock()
    }
}

while true {
    consume()
    sleep(1)
    produce()
}
  • NSConditionLock:條件鎖,對(duì) NSCondition 的進(jìn)一步封裝。
// 初始化時(shí)condition為0
let lock = NSConditionLock(condition: 0)
var products = [Int]()

// 消費(fèi)者
func consume() {
    DispatchQueue.global().async {
        // 加鎖,當(dāng)參數(shù)與初始化時(shí)condition不一致時(shí)進(jìn)行等待
        lock.lock(whenCondition: 1)
        // 消費(fèi)產(chǎn)品
        let product = products.remove(at: 0)
        print("消費(fèi)產(chǎn)品\(product)")
        // 解鎖,修改condition的值為0
        lock.unlock(withCondition: 0)
    }
}

// 生產(chǎn)者
func produce() {
    DispatchQueue.global().async {
        // 加鎖,與初始化時(shí)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) {
    // 加鎖(換成其他的鎖會(huì)死鎖)
    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("結(jié)束")
}

objc_sync


let lock: Int = 0
// 加鎖
objc_sync_enter(lock) // 很多時(shí)候參數(shù)為self

// 臨界區(qū)

// 解鎖
objc_sync_exit(lock)

OSSpinLock自旋鎖

由于存在因?yàn)榈蛢?yōu)先級(jí)爭奪資源導(dǎo)致死鎖的問題,所以在 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)

信號(hào)量

DispatchSemaphore 是一種基于計(jì)數(shù)的信號(hào)量。它可以設(shè)定一個(gè)閥值,多個(gè)線程競爭獲取許可信號(hào),超過閥值后,線程申請(qǐng)?jiān)S可信號(hào)將會(huì)被阻塞。主要用于線程之間的數(shù)據(jù)同步。

  • DispatchSemaphore(value: ):創(chuàng)建信號(hào)量,value 為初始值。
  • wait:根據(jù)當(dāng)前信號(hào)量的值進(jìn)行判斷:
    (1)若大于 0,則將信號(hào)量減 1 ,繼續(xù)執(zhí)行后續(xù)任務(wù)。
    (2)若小于等于 0,則阻塞當(dāng)前線程,直到信號(hào)量大于 0 或者經(jīng)過一個(gè)閾值時(shí)間才會(huì)執(zhí)行后續(xù)任務(wù)。
  • signal:信號(hào)量加 1。

DispatchSemaphore

// 創(chuàng)建信號(hào)量,初始值為0
let semaphore = DispatchSemaphore(value: 0)

// 線程業(yè)務(wù)代碼
DispatchQueue.global().async {
    // 臨界區(qū)

    semaphore.signal()
}

semaphore.wait(timeout: .distantFuture)

UI更新問題

  • 當(dāng) App 運(yùn)行以后,主線程隨之啟動(dòng)。該線程需要接收用戶的交互,完成界面的更新等操作,因此必須保證它的流暢性,耗時(shí)的操作不能放在主線程中執(zhí)行,否則會(huì)造成界面的卡頓甚至崩潰。
  • iOS 規(guī)定不能在子線程中更新 UI 界面,更新 UI 的操作必須在主線程中進(jìn)行。如果在子線程中更新了 UI,程序在編譯時(shí)并不會(huì)報(bào)錯(cuò),但運(yùn)行時(shí)會(huì)出現(xiàn)意料不到的結(jié)果甚至崩潰,此時(shí)控制臺(tái)和 Xcode 也會(huì)有相應(yīng)的錯(cuò)誤信息輸出和提示。
  • 針對(duì) 3 種不同的線程實(shí)現(xiàn)方式,回到主線程也有 3 種方式。
import UIKit

// MARK:- Thread模式
func threadMode(){    
    let thread =  Thread {  
        print("\(Thread.current)執(zhí)行任務(wù)")
        // 休眠
        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í)行任務(wù)")
        // 休眠
        sleep(3)
        // 更新UI
        DispatchQueue.main.async {          
            self.infoLb.text = "GCD方式更新UI"
        }  
    }   
}



// MARK:- Operation模式
func operationMode(){    
    OperationQueue().addOperation {   
        print("\(Thread.current)執(zhí)行任務(wù)")
        // 休眠
        sleep(3)
        // 更新UI
        OperationQueue.main.addOperation {          
            self.infoLb.text = "Operation方式更新UI"
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容