Swift多執行緒:使用Thread進行多執行緒間通訊,協調子執行緒任務

非典型技術宅發表於2019-02-27

Swift的多執行緒技術其實和Objective-C沒有區別。Thread是三種正常程式設計師會使用的多執行緒中最輕量級的,每一個Thread物件代表著一個執行緒,但是需要自己管理執行緒的生命週期和執行緒的同步。執行緒同步對資料的加鎖會有一定的開銷。

哪三種正常程式設計師會使用的多執行緒方案,問這個的同學,你,出去~ 前面寫了辣麼多,Operation、GCD、Thread,都忘啦?

如果要是較真,還有一個多執行緒技術,叫做pthread。我們會在最後最後稍微說一下它。但是它絕對不是正常程式設計師現在還需使用的多執行緒方案。說完這句話不知道會不會被噴死

1. Thread的三種建立方式

以下所有的程式碼都是使用陳舊的Swift 3.0編寫。

對,你沒看錯。陳舊的Swift 3.0。 Swift 5.0 都開始了,聽到這個訊息的時候,內心是五味雜陳的,有點點為Swift擔心了。尼瑪這4.0 釋出沒多久,5.0就開始了。
所以坊間才有笑話說,學習iOS開發需要熟練使用Swift1.0 , Swift2.0 ,Swif 2.2…四種語言之類的。

1.1 使用類方法建立,自動執行

一種是帶Selector,一種不帶。

Thread.detachNewThread {
    print("A new thread,name:(Thread.current)")
}


//帶一個Selector
       Thread.detachNewThreadSelector(#selector(threadPrint), toTarget: self, with: nil)
複製程式碼

通過這種方法建立的thread會自動執行。

1.2 實用構造方法創始化,需手動執行

這裡可是使用兩種方法直接建立Thread,並執行。

Thread(block: <#T##() -> Void#>)
Thread(target: <#T##Any#>, selector: <#T##Selector#>, object: <#T##Any?#>)
複製程式碼

下面是個例子,這種方法建立的thread需要手動start執行

   let customThread = Thread(target: self, selector: #selector(threadPrint), object: nil)
    customThread.start()
複製程式碼

1.3 使用NSObject的擴充方法

任何繼承自NSObject的都可以很容易的通過這種方式呼叫點成方法。

來,我們一起來看一下原始檔:

extension NSObject {

    
    open func performSelector(onMainThread aSelector: Selector, with arg: Any?, waitUntilDone wait: Bool, modes array: [String]?)

    open func performSelector(onMainThread aSelector: Selector, with arg: Any?, waitUntilDone wait: Bool)

    
    @available(iOS 2.0, *)
    open func perform(_ aSelector: Selector, on thr: Thread, with arg: Any?, waitUntilDone wait: Bool, modes array: [String]?)

    @available(iOS 2.0, *)
    open func perform(_ aSelector: Selector, on thr: Thread, with arg: Any?, waitUntilDone wait: Bool)

    
    @available(iOS 2.0, *)
    open func performSelector(inBackground aSelector: Selector, with arg: Any?)
}
複製程式碼

2. Thread的基本使用

Thread的基本使用相當簡單,和GCD基本上差不多。也有啟動、暫停、取消、阻塞、設定優先順序等等。

方法 作用
start 啟動
cancel 暫停
exit 取消
sleep 阻塞

Thread的常用屬性

名稱 用途
name 給執行緒命名,方便查詢
stackSize 棧區大小,看看執行緒在棧區佔了多大空間
isMainThread 是否是主執行緒
qualityOfService 服務質量,iOS8.0推出,為了取代優先順序。
  • 說明:thread設定了start後,其實並不是馬上就開始執行了。實質上是放進了可排程執行緒池,等待被CPU呼叫。執行緒執行結束之前,狀態可能會在就緒狀態 和 執行狀態 之間來回的切換
    就緒狀態 和 執行狀態 之間的狀態切換由CPU來完成,程式設計師無法干涉。

  • 阻塞:正在執行thread可以通過sleep的方式來阻塞執行緒的執行。

  • 退出:thread在執行完畢之後會自動退出。如果執行了exit,執行緒會強制退出。有幾點需要注意一下:

    • 不要在主執行緒中呼叫啊,會讓UI執行緒退出的。退出之後看你怎麼搞!
    • 退出之後,這個執行緒剩下的所有程式碼都不會被執行。
    • 呼叫這個方法之前,一定要注意釋放之前由C語言建立的物件,不然會造成記憶體洩漏等問題。
  • 取消:這個cancel和GCD一樣的啦,並沒有真證的取消執行緒,只是打了一個標誌。取消需要自己的實現。也就是在大人物開始之前,先判斷一下這個標誌位的狀態。要是從來都沒寫過這個標誌位的狀態判斷,那cancel了也是白瞎。

3. 使用NSCondition實現執行緒間通訊

大家還記得GCD中的訊號量(semaphore)嘛?不記得話看看嘍,傳輸門:Swift多執行緒:GCD進階,單例、訊號量、任務組 。Thread裡面的NSCondition和這個有點像。

這個玩意一共就四個方法,我們索性都來看看:

方法名稱 作用
wait 使執行緒處於等待狀態
wait(until limit: Date) -> Bool 在給定的時間到達時仍未有訊號量出現, 就自動繼續了
signal 喚醒執行緒
broadcast 喚醒所有等待執行緒

NSCondition實現了NSLocking協議,所以它本身也有lock和unlock方法。配合在一起可以解決執行緒同步的問題,只要線上程開始時加鎖,取得資源後釋放鎖即可。使用時把需要加鎖的程式碼放到lockunlock之間就可以了。

主要不要把什麼亂七八糟的都往加鎖程式碼裡面放,放在這個裡面的應該是搶佔資源的讀取和修改。不然一個執行緒執行的時候另一個執行緒就一直在等待,那還要多執行緒幹哈玩意?!

我們搞個例子來看看。

需求:

1,模擬下載五張圖片、五篇文章;

2,圖片下載了兩張之後,暫停下載,轉而開啟文章下載;

3,下載三篇文章之後,暫停下載,轉而繼續下載剩下的三張圖片。

4,圖片下載完成後,下載完成剩下的兩篇文章。

說了是模擬啊,所以下載過程就直接print了噢。

class ViewController: UIViewController {
    var downImages: Thread?
    var downArticles: Thread?
    
    let imageCondition = NSCondition()
    let articleCondition = NSCondition()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        downImages = Thread(target: self, selector: #selector(downloadImages), object: nil)
        downArticles = Thread(target: self, selector: #selector(downloadArticles), object: nil)
        downImages?.start()
        
    }
    
    @objc private func threadPrint() {
        Thread.sleep(forTimeInterval: 2)
        print("After 2 seconds, I have been performed. I am (Thread.current)")
    }
    
    @objc fileprivate func downloadImages() {
        for index in 1...5 {
            print("Downloading No.(index) image.")
            Thread.sleep(forTimeInterval: 1)
            
            if index == 2 {
                //start downArticles.開啟下載文章的執行緒
                downArticles?.start()
                
                //Lock the image thread.加鎖,讓下載圖片的執行緒進入等待狀態
                imageCondition.lock()
                imageCondition.wait()
                imageCondition.unlock()
            }
        }
        print("All images have been completed.")
        
        //Signaling the article when all images completed.
//        等圖片都下載完成之後,啟用下載文章的程式
        articleCondition.signal()
    }
    
    @objc fileprivate func downloadArticles() {
        for index in 1...5 {
            print("The No.(index) article will be downloading.")
            Thread.sleep(forTimeInterval: 1)
            if index == 3 {
                //Signaling the image thread, let it continue to down.
                //啟用圖片的執行緒,讓它繼續下載圖片
                imageCondition.signal()
                
                //Lock the article thread.加鎖,讓下載文章的執行緒進入等待狀態
                articleCondition.lock()
                articleCondition.wait()
                articleCondition.unlock()
                
            }
        }
        print("There are 5 articles.")
        
    }
}
複製程式碼

我們列印一下最終的列印結果:

Swift多執行緒:使用Thread進行多執行緒間通訊,協調子執行緒任務

好了。最後再說一下基本上沒人用的pthread

4. pthread

其實不知道這個多執行緒的技術現在還有誰會在用,除了面試可能會偶爾問一下這個名詞。
pthread是POSIX thread的簡寫。表示跨平臺的執行緒介面。

為了能夠寫Demo,非典型技術宅在浩瀚的Google海洋中游蕩了很久,然後,然後,然後放棄了…

怎麼能這麼不堅持吶,在OC時代曾經還是能寫出來建立的。於是去看蘋果手冊,然後,然後,然後放棄了…

Threading Programming Guide 。這裡倒是提到了POSIX thread ,還讓人家去看pthread man page。但是,明顯連結被刪除了。

蘋果爸爸。。。好想錘你小胸口。好吧,放棄了。

最後,所有的程式碼都放在這裡了:gitHub 下載後給顆Star吧~ 麼麼噠~(~o ̄3 ̄)~ 愛你們~

相關文章