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方法。配合在一起可以解決執行緒同步的問題,只要線上程開始時加鎖,取得資源後釋放鎖即可。使用時把需要加鎖的程式碼放到lock
和unlock
之間就可以了。
主要不要把什麼亂七八糟的都往加鎖程式碼裡面放,放在這個裡面的應該是搶佔資源的讀取和修改。不然一個執行緒執行的時候另一個執行緒就一直在等待,那還要多執行緒幹哈玩意?!
我們搞個例子來看看。
需求:
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.")
}
}
複製程式碼
我們列印一下最終的列印結果:
好了。最後再說一下基本上沒人用的pthread
。
4. pthread
其實不知道這個多執行緒的技術現在還有誰會在用,除了面試可能會偶爾問一下這個名詞。 pthread是POSIX thread的簡寫。表示跨平臺的執行緒介面。
為了能夠寫Demo,非典型技術宅在浩瀚的Google海洋中游蕩了很久,然後,然後,然後放棄了...
怎麼能這麼不堅持吶,在OC時代曾經還是能寫出來建立的。於是去看蘋果手冊,然後,然後,然後放棄了...
Threading Programming Guide 。這裡倒是提到了POSIX thread ,還讓人家去看pthread man page
。但是,明顯連結被刪除了。
蘋果爸爸。。。好想錘你小胸口。好吧,放棄了。
最後,所有的程式碼都放在這裡了:gitHub 下載後給顆Star吧~ 麼麼噠~(~o ̄3 ̄)~ 愛你們~