NSThread
這套方案是經過蘋果封裝后的,并且完全面向對象的。所以你可以直接操控線程對象,非常直觀和方便。但是,它的生命周期還是需要我們手動管理,所以這套方案也是偶爾用用,比如 NSThread.currentThread()
,它可以獲取當前線程類,你就可以知道當前線程的各種屬性,用于調試十分方便。
在做定時器的時候也可以用用,只是用多線程啟動,并不需要關心線程本身。
創建并啟動
- 先創建線程類,再啟動
let thread = NSThread(target: self, selector: "run:", object: nil)
//啟動
thread.start()```
* 創建并自動啟動
NSThread.detachNewThreadSelector("run:", toTarget: self, withObject: nil)
* 使用 NSObject 的方法創建并自動啟動
self.performSelectorInBackground("run:", withObject: nil)
>Swift中,這個方法不推薦使用。
##其他方法
//取消線程
public func cancel()
//啟動線程
public func start()
//判斷某個線程的狀態的屬性
public var executing: Bool { get }
public var finished: Bool { get }
public var cancelled: Bool { get }
//設置和獲取線程名字
public var name: String?
//獲取當前線程信息
public class func currentThread() -> NSThread
//獲取主線程信息
public class func mainThread() -> NSThread
//使當前線程暫停一段時間,或者暫停到某個時刻
public class func sleepForTimeInterval(ti: NSTimeInterval)
public class func sleepUntilDate(date: NSDate)
#NSOperation和NSOperationQueue
```NSOperation``` 是蘋果公司對 ```GCD``` 的封裝,完全面向對象。操作步驟:
1. 將要執行的任務封裝到一個 ```NSOperation``` 對象中
。
2. 將此任務添加到一個 ```NSOperationQueue``` 對象中
。
##普通用法
//1.創建隊列
let queue = NSOperationQueue()
//2.創建NSBlockOperation對象
let operation = NSBlockOperation { () -> Void in
NSLog("%@", NSThread.currentThread())
}
//3.添加多個Block
for i in 0..<5 {
operation.addExecutionBlock { () -> Void in
NSLog("第%ld次 - %@", i, NSThread.currentThread())
}
}
//4.隊列添加任務
queue.addOperation(operation)```
多線程環境下,打印輸出用
NSLog()
,不用print()
任務依賴
比如有 3 個任務:A: 從服務器上下載一張圖片,B:給這張圖片加個水印,C:把圖片返回給服務器。這時就可以用到依賴了:
//1.任務一:下載圖片
let operation1 = NSBlockOperation { () -> Void in
NSLog("下載圖片 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//2.任務二:打水印
let operation2 = NSBlockOperation { () -> Void in
NSLog("打水印 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//3.任務三:上傳圖片
let operation3 = NSBlockOperation { () -> Void in
NSLog("上傳圖片 - %@", NSThread.currentThread())
NSThread.sleepForTimeInterval(1.0)
}
//4.設置依賴
operation2.addDependency(operation1) //任務二依賴任務一
operation3.addDependency(operation2) //任務三依賴任務二
//5.創建隊列并加入任務
let queue = NSOperationQueue()
queue.addOperations([operation3, operation2, operation1], waitUntilFinished: false)
從其他線程回到主線程
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in
}```
#GCD
它是蘋果為多核的并行運算提出的解決方案,所以會自動合理地利用更多的CPU內核(比如雙核、四核),最重要的是它會自動管理線程的生命周期(創建線程、調度任務、銷毀線程),完全不需要我們管理,我們只需要告訴干什么就行。
##普通用法
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//這里寫需要大量時間的代碼
print("這里寫需要大量時間的代碼")
dispatch_async(dispatch_get_main_queue(), {
//這里返回主線程,寫需要主線程執行的代碼
print("這里返回主線程,寫需要主線程執行的代碼")
})
})
## 創建隊列
* 主隊列
let queue = ispatch_get_main_queue()
* 自己創建的隊列
//串行隊列
let queue = dispatch_queue_create("tk.bourne.testQueue", nil);
let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL)
//并行隊列
let queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT)
* 全局并行隊列
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
>只要是并行任務一般都加入到這個隊列。這是系統提供的一個并發隊列。
##創建任務
* 同步任務:會阻塞當前線程 (SYNC)
dispatch_sync(<#queue#>, { () -> Void in
//code here
println(NSThread.currentThread())
})
異步任務:不會阻塞當前線程 (ASYNC)
dispatch_async(<#queue#>, { () -> Void in
//code here
println(NSThread.currentThread())
})
###示例一:
以下代碼在主線程調用,結果是什么?
NSLog("之前 - %@", NSThread.currentThread())
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
NSLog("之后 - %@", NSThread.currentThread())
####答案:
只會打印第一句:```之前 - <NSThread: 0x7fb3a9e16470>{number = 1, name = main}``` ,然后主線程就卡死了,你可以在界面上放一個按鈕,你就會發現點不了了。
####解釋:
同步任務會阻塞當前線程,然后把 Block 中的任務放到指定的隊列中執行,只有等到 Block 中的任務完成后才會讓當前線程繼續往下運行。
那么這里的步驟就是:打印完第一句后,```dispatch_sync``` 立即阻塞當前的主線程,然后把 Block 中的任務放到 ```main_queue``` 中,可是 ```main_queue``` 中的任務會被取出來放到主線程中執行,但主線程這個時候已經被阻塞了,所以 Block 中的任務就不能完成,它不完成,```dispatch_sync``` 就會一直阻塞主線程,這就是死鎖現象。導致主線程一直卡死。
###示例二:
以下代碼會產生什么結果?
let queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL)
NSLog("之前 - %@", NSThread.currentThread())
dispatch_async(queue, { () -> Void in
NSLog("sync之前 - %@", NSThread.currentThread())
dispatch_sync(queue, { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
NSLog("sync之后 - %@", NSThread.currentThread())
})
NSLog("之后 - %@", NSThread.currentThread())
####答案:
```2015-07-30 02:06:51.058 test[33329:8793087] 之前 - <NSThread: 0x7fe32050dbb0>{number = 1, name = main}
2015-07-30 02:06:51.059 test[33329:8793356] sync之前 - <NSThread: 0x7fe32062e9f0>{number = 2, name = (null)}
2015-07-30 02:06:51.059 test[33329:8793087] 之后 - <NSThread: 0x7fe32050dbb0>{number = 1, name = main}```
很明顯 ```sync - %@``` 和 ```sync之后 - %@``` 沒有被打印出來!這是為什么呢?我們再來分析一下:
####分析:
1. 使用 DISPATCH_QUEUE_SERIAL 這個參數,創建了一個 串行隊列。
2. 打印出 ```之前 - %@ ```這句。
3. ```dispatch_async``` 異步執行,所以當前線程不會被阻塞,于是有了兩條線程,一條當前線程繼續往下打印出 ```之后 - %@```這句, 另一條執行 Block 中的內容打印 ```sync之前 - %@``` 這句。因為這兩條是并行的,所以打印的先后順序無所謂。
4. ```dispatch_sync```同步執行,于是它所在的線程會被阻塞,一直等到 ```sync``` 里的任務執行完才會繼續往下。于是 ```sync``` 就把自己 Block 中的任務放到 ```queue``` 中,可 ```queue``` 是一個串行隊列,一次執行一個任務,所以 ```sync``` 的 Block 必須等到前一個任務執行完畢,可是 ```queue``` 正在執行的任務就是被 ```sync``` 阻塞了的那個。于是又發生了死鎖。所以 ```sync``` 所在的線程被卡死了。剩下的兩句代碼自然不會打印。
## 隊列組
隊列組可以將很多隊列添加到一個組里,這樣做的好處是,當這個組里所有的任務都執行完了,隊列組會通過一個方法通知我們。下面是使用方法,這是一個很實用的功能。
//1.創建隊列組
let group = dispatch_group_create()
//2.創建隊列
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
//3.多次使用隊列組的方法執行任務, 只有異步方法
//3.1.執行3次循環
dispatch_group_async(group, queue) { () -> Void in
for _ in 0..<3 {
NSLog("group-01 - %@", NSThread.currentThread())
}
}
//3.2.主隊列執行8次循環
dispatch_group_async(group, dispatch_get_main_queue()) { () -> Void in
for _ in 0..<8 {
NSLog("group-02 - %@", NSThread.currentThread())
}
}
//3.3.執行5次循環
dispatch_group_async(group, queue) { () -> Void in
for _ in 0..<5 {
NSLog("group-03 - %@", NSThread.currentThread())
}
}
//4.都完成后會自動通知
dispatch_group_notify(group, dispatch_get_main_queue()) { () -> Void in
NSLog("完成 - %@", NSThread.currentThread())
}
##從其他線程回到主線程
dispatch_async(dispatch_get_main_queue(), { () -> Void in
})```
延時執行
var time = dispatch_time(DISPATCH_TIME_NOW, (Int64)(10 * NSEC_PER_SEC))
dispatch_after(time, globalQueue) { () -> Void in
println("在10秒后執行")
}
NSEC_PER_SEC
表示的是秒數,它還提供了NSEC_PER_MSEC
表示毫秒。