Swift5.x 多線程編程


//
//  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 需要再主線程等待一個異步任務返回,才能繼續執行下面的邏輯,但是又不希望堵塞用戶事件。



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM