//
// ViewController16.swift
// swiftT
//
// Created by wjwdive on 2020/6/1.
// Copyright © 2020 wjwdive. All rights reserved.
//
import UIKit
class ViewController16: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//1 Thread多線程打印1到10
for i in 0..<10 {
Thread.detachNewThread{
print(i)//打印是無序的,
}
}
//2
let obj = ObjectThread()
obj.threadTest()//
//Operation queue 創建隊列並添加操作
let operation = ObjectForThread()
operation.threadTest()
//completionBlock
let operationCallBack = ObjectCompletionBlock()
operationCallBack.threadTest()
//qos 服務質量
//DispatchQoS.default
//DispatchQoS.background
//DispatchQoS.userInitiated
//DispatchQoS.userInteractive
//DispatchQoS.unspecified
//
// 創建一個隊列
let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency:
DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
//同步執行一個任務
queue.sync {
print("in queu sync")
}
print("after invoke queue method!")
//延遲執行
queue.asyncAfter(deadline: .now() + 2) {
print("asyncAfter")
}
//系統時鍾 關機之后系統時鍾會暫停
//DispatchTime
//牆上掛的時鍾 ,不關注系統時間,關機計算時間
//DispatchWallTime
let group = DispatchGroup()
group.enter()
queue.async {
sleep(1)
print("模擬接口1請求完成")
group.leave()
}
group.enter()
queue.async {
sleep(1)
print("模擬接口2請求完成")
group.leave()
}
print("group enter() 調用完成")
group.wait()//阻塞
print("接口1,接口2調用完成")
let groupn = DispatchGroup()
groupn.enter()
print("groupn enter() 調用完成")
queue.async {
sleep(1)
print("模擬接口A請求完成")
groupn.leave()
}
groupn.enter()
queue.async {
sleep(1)
print("模擬接口B請求完成")
groupn.leave()
}
print("groupn enter() 調用完成")
groupn.notify(queue: queue) {
print("接口A,接口B調用完成")
}
print("驗證不阻塞")
//timer
var seconds = 10
let timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
timer.schedule(deadline: .now(), repeating: 1.0)
timer.setEventHandler {
seconds -= 1
if seconds < 0 {
timer.cancel()
} else {
print(seconds)
}
}
timer.resume()
//串行隊列里先同步調一個任務,任務里再同步調一個任務
lockSimple()
}
}
// Swift多線程編程
// 思考: 為什么幾乎所有的GUI框架都是單線程的?
// 回答: 因為多線程會有線程調度,雖然多線程會加快運算速度,但是帶來的線程調度和開銷,資源搶占,死鎖等問題會更多。
// 多線程編程方式:【3種】
// Thread, Cocoa Operation, GCD(Grand Central Dispatch)
// Thread,在三種多線程技術中是最輕量級的,但需要自己管理自己線程的生命周期和線程同步。線程同步對數據的枷鎖會有一定的系統開銷。
//
//Thread 創建線程的3種方式
func ThreadCreat1() {
//1
Thread.detachNewThread {
print("detachNewThread creat a therad!")
}
}
//
//func ThreadCreat2() {
// //2
// Thread.detachNewThreadSelector(#seletor(funcName): Selector, toTarget: self, with: nil) {
//
// }
//
//}
//
//func ThreadCreat3() {
// //3
// Thread(target: self., selector: #seletor(funcName), object: nil)
//}
//在Playground里需要添加以下聲明
//import PlaygroundSupport
//PlaygroundPage.current.needIndefiniteExecution = true
class ObjectThread {
func threadTest() {
let thread = Thread(target: self, selector: #selector(threadWorker), object: nil)
//使用初始化器創建的線程需要手動start
thread.start()
}
@objc func threadWorker() {
print("threadWorker")
}
}
//面向對象
// Operation + OperationQueue
//取消,依賴,任務優先級,復雜邏輯,保存業務子狀態,子類化
//Operation, Block Operation
//Operation四個狀態(屬性)
//1、 isReady
//2、 isExecuting
//3、 isFinished
//4、 isCancelled
// OPeration 運行方式
// 1、sync 同步 在main方法里
// 2、async 異步,比如網絡請求
// start()
// isAsynchromous
// isExecuting
// isFinished
// Operation可以添加依賴
//func addDependency(Operation)
//func removeDependency(Operation)
//var dependencied:[Operation]
//Operation Queue
//1、Operation Queue隊列里可以加入很多個Operation, 可以把OperationQueue看作一個線程池,可以往線程池中添加操作(Operation)到隊列中
//2、底層使用GCD
//3、maxConcurrentOperationCount 可以設置最大並發數【最多不超過5個】
//4、defaultMaxConcurrentOperationCount 根據當前系統條件動態確定的最大並發數
//5、可以取消所有Operation,但是當前正在執行的不會執行
//6、所有Operation執行完畢后退出銷毀
//BlockOperation
class ObjectForThread {
func threadTest() {
let operation = MyOperation()
let queue = OperationQueue()
queue.addOperation(operation)
}
}
class MyOperation: Operation {
override func main() {
sleep(1)
print("MyOperation")
}
}
//completionBlock 帶回調的OperationBlock
class ObjectCompletionBlock {
func threadTest() {
let operation = MyOperation()
operation.completionBlock = {()-> Void in
print("---operation.completionBlock---")}
let queue = OperationQueue()
queue.addOperation(operation)
print("threadTest")
}
}
//封裝一個網絡請求
//GCD
// 任務+隊列
// 易用,高效,性能
//功能:
//1、創建管理Queue
//2、提交Job
//3、Dispatch Group
//4、管理Dispatch Object
//5、信號量 Semaphore
//6、隊列屏障
//7、Dispatch Source
//8、Queue Context 數據
//9、Dispatch I/O Channel
//10、Dispatch Data 對象
// Dispatch.main
// Dispatch.global
// DispatchQueue(label:,qos:,autoreleaseFrequency:,target:) //自己創建
// queue.label
// setTarget(queue: DispatchQueue?)
//GCD-隊列
//1、最終的目標隊列都是主隊列和全局隊列
//2、如果把一個並行隊列的目標隊列設置成一個串行隊列,那么並行隊列將不再並行
//3、如果多個隊列的目標隊列都設置為同一個串行隊列,那么這多個隊列連同b目標隊列里的任務都將穿行執行
//4、如果設置目標隊列形成了環,結果是不可預期的
//5、如果在一個隊列正在執行任務的時候更換目標隊列,結果也是不可預期的
//GCD里的基本操作
//1、穿行和並行:描述任務之間是如何運行的,串行任務每次僅僅執行一個,並行任務可以多個同時執行
//2、同步和異步: 描述的是任務的執行過程是如何的。同步:同一個過程會等待
//同步任務
// A
// |
// B
// |
// A
//異步任務
// A
// |\
// A B
// | /
// A
// GCD-sync
// 提交任務到當前隊列里,並且直到任務執行完成,當前隊列才返回
//func sync(execute: DispatchWorkItem)
// submints a work for execution on the current queue and returns after that block finished executing
//func sync(execute: () -> Void)
// submits a block object for executino and returns after that block finishes executing
//func sync<T>(execute: () -> T) -> T
//submits a work item for execution and returns the results from that item after it finishes executing
//func sync<T>(flags: DispatchWorkItemFlags, execute:() -> T) -> T
//Submits a work item for execution using the specified attributes and returns the results from that item after it finishes executing
//GCD-async
//調度一個任務立即取執行,但是不用等待任務執行完當前隊列就會返回
//GCD-asyncAfter
//調度一個任務多久之后執行,但是不用等待任務執行完當前隊列就會返回
//DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
//執行的任務
//print("延遲2.5s執行的任務")
//}
//DispatchGroup
//enter和leave必須成對出現
//DispatchGroup-wait
//DispatchGroup-notify
//DispatchSource
//dispatch source是一個監視某些類型事件的對象。這些事件發生時,它自動將一個task放入一個dispatch queue的執行歷程中
//用途
//Mach port send right state changes
//Mach port receive right state changes
//External process state change
//File descriptor ready for read
//File descriptor ready for write
//Filesystem node event.
//PPSIX signal
//custom timer
//custom event
// GCD源碼剖析
// 一個死鎖的例子
//串行隊列,異步執行一個任務,任務里又串行執行一個任務
func lockSimple() {
let queue = DispatchQueue(label: "serial queue")
queue.async {
print("in queue async")
queue.async {
print("in queue sync")
}
}
}
//並行隊列不會死鎖了
func noLockSimple() {
let queueCuncurrent = DispatchQueue(label: "concurrent queue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
queueCuncurrent.async {
print("in queueCuncurrent async")
queueCuncurrent.sync {
print("in queueCuncurrent sync")
}
}
}
// 死鎖
//資源競爭或彼此通訊 死鎖時指兩個或兩個以上的進程在執行過程中,由於競爭資源或者由於彼此通信而造成的一種阻塞現象,若無外力作用,它們都將無法推進下去。此時系統處於死鎖狀態或者系統產生了死鎖。
//例子:線程A 在t1時刻占用了X資源(t1開始A鎖定了X),線程B在t2時刻占用了Y資源(t2開始B鎖的了Y),t3時刻線程A又要嘗試占用Y資源(資源Y t2 時刻就已被線程B鎖的),t4時刻線程B又要嘗試占用資源X,這時候就會發生死鎖。AB線程都個字擁有了資源但又需要對方已經在使用的資源,就會發生死鎖。(時刻:t1 < t2 < t3 < t4)
// 臨界區:
// 就是一段代碼不能被並發執行,也就是,兩個線程不能同時執行這段代碼
// 靜態條件
// 兩個或多個線程讀寫某些共享數據,而最后的結果取決於線程運行時的精確時序。
// 例如多個線程讀寫銀行存款余額,
// 優先級反轉
// 假如有高A-中B-低C3種優先級的線程,t1時刻低優先級的線程C先獲取了一個共享資源S,t2時刻,高優先級的線程A嘗試獲取資源S,但是線程C還在占用資源S,所以A就被掛起了。這時B就有了最高可運行優先級,B在C運行完之后會立即獲取到資源S(A還在掛起狀態),B繼續占有S運行,期間A會繼續間隔嘗試獲取S,但是得不到S無法運行,期間C若要再次運行,他沒有B的優先級高也得不到S.所以優先級排序ABC變成了BAC。也就是優先級反轉
// 並發與並行
// 並行:一般發生在CPU多核多線程的情況下,兩個線程在兩個核上同時執行,而不需要間斷
// 並發:需要有一些對線程持續的調度,或者有一些上下文的切換,來看起來像Thread1,Thread2同時執行。但實際上是兩個線程相互讓渡系統執行事件來達到的一種假象。
//
//iOS中的鎖
//1 OSSpinLock
//2 dispatch_semaphore
//3 pthread_mutex
//4 NSLock
//5 NSCondation
//6 pthread_mutex(recursive)
//7 NSRecursiveLock
//8 NSConditionLock
//9 @synchronized swift 中已被移除
//1 OSSpinLock:
// 線程是通過busy-wait-loop的方式來獲得鎖的,任意時刻只有一個線程能獲得鎖,其他線程忙等待直到獲得鎖。
// 普通的鎖實現,當一個線程加鎖一個資源時,其他的線程再訪問資源,就獲取不到,就會進入休眠狀態,直到活躍線程執行完,等待系統把休眠的線程喚醒,才能給資源加上鎖繼續執行。但是OSSpinLock任意時刻只有一個線程能獲得鎖,其他線程忙等待直到獲得鎖。
//bool lock = false //一開始沒有鎖上,任何線程都可以申請鎖
//do {
// while(test_and_set(&lock));//test_and_set是一個原子操作
// Critical section;//臨界區
// lock = fasle // 相當於釋放鎖,這樣別的線程可以進入臨界區
// Reminder section //不需要鎖保護代碼
//}
//OSSpinLock適用范圍
//1、臨界區盡量簡短,控制在100行代碼以內,不要有顯式或隱式的系統調用,調用的函數也盡量簡短。因為執行臨界區代碼非常長的話,所有的等待進入臨界區的線程訪問臨界區的時候都會忙等待,造成資源浪費或卡頓。
//2、保證訪問鎖的線程全部都處於同一優先級。可能會出現優先級反轉
//9 @synchronized swift 中已被移除,不過可以自己實現該功能
func synchorized(_ obj: AnyObject, closure: () -> () ) {
objc_sync_enter(obj)
closure()
objc_sync_exit(obj)
}
//注意點:
//1 只有傳同樣的synchronized,才能起到加鎖作用
//2 如果傳 nil ,無法起到加鎖的作用
//3 可以重用
//4 synchorized不會持有傳給他的對象
//objc_sync_enter的實現
//int objc_sync_enter(id obj) {
// int result = OBJC_SYNC_SUCCESS;
// if(obj) {
// SyncData *data = id2data(obj, ACQUIRE);
// assert(data);
// data->mutex.lock();
// }else {
// //@synchorized(nil) does nothing
// if(DebugNilSync) {
// _objc_inform("NIL SYNC DEBUG: @synchorized(nil);set a breadpoint on 0bjc_sync_nil to debug")
// }
// objc_sync_nil();
//
// }
// return result;
//}
// end synchorizing on 'obj'.
// Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
//int objc_sync_exit(id obj){
// int reuslt = OBJC_SYNC_SUCCESS;
// if(obj) {
// SyncData *data = id2data(obj, RELEASE);
// if(!data) {
// result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
// }else {
// bool okay = data->mutex.tryUnlock();
// if(!okay) {
// result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
// }
// }
// } else {
// // @synchorized(nil) does nothing
// }
// return result
//}
// SyncData是什么?
//1 SyncData是可以重用的,(threadCount == 0)
//2 存在全局的map里
//SyncData 定義
//typedef struct alignas(ChaheLineSize) SyncData {
// struct SyncData* nextData;
// DisguisedPtr<ojc_object> object;
// int32_t threadCount;
// recursive_mutext_t mutex;
//} SyncData;
//如果是對 ObjcA, ObjcB, ObjcC 三個對象進行synchorized操作,其中先對ObjcA操作兩次。
//多線程的應用場景
//1 一個也沒有三個網絡請求,需要再三個網絡請求返回的時候刷新界面
//2 需要一個線程安全的Array的讀寫
//3 編寫一個多線程下載器,可執行多個下載任務,每個任務可以保存當下下載的字節數,總字節數,可以設置回調得到當前下載進度
//4 需要再主線程等待一個異步任務返回,才能繼續執行下面的邏輯,但是又不希望堵塞用戶事件。