多線程

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表示毫秒。

參考文獻

一篇比較好的關于多線程的文章

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容

  • 在這篇文章中,我將為你整理一下 iOS 開發中幾種多線程方案,以及其使用方法和注意事項。當然也會給出幾種多線程的案...
    張戰威ican閱讀 612評論 0 0
  • 在這篇文章中,我將為你整理一下 iOS 開發中幾種多線程方案,以及其使用方法和注意事項。當然也會給出幾種多線程的案...
    伯恩的遺產閱讀 274,753評論 251 2,331
  • 從哪說起呢? 單純講多線程編程真的不知道從哪下嘴。。 不如我直接引用一個最簡單的問題,以這個作為切入點好了 在ma...
    Mr_Baymax閱讀 2,818評論 1 17
  • 學習多線程,轉載兩篇大神的帖子,留著以后回顧!第一篇:關于iOS多線程,你看我就夠了 第二篇:GCD使用經驗與技巧...
    John_LS閱讀 634評論 0 3
  • 歲月迢迢,往事遙遙。啟書頁,來誦靈韶。桑田易朽,詩韻難消。有漢時風,唐時律,宋時謠。 盛衰有序,天道何逃?繁華景,...
    徐嘯閱讀 280評論 0 0