理論基礎(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è)重要的概念:
- 臨界資源:一次只能允許一個(gè)線程使用的共享資源。
- 臨界區(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"
}
}
}