一、前言
上一篇文章iOS多線程淺匯-原理篇中整理了一些有關(guān)多線程的基本概念。本篇博文介紹的是iOS中常用的幾個(gè)多線程技術(shù):
NSThread
GCD
NSOperation
由于apple不提倡開發(fā)者直接對(duì)線程進(jìn)行操作,日常開發(fā)過(guò)程中GCD和NSOperation的使用也較多,因此NSThread會(huì)介紹得少些,主要篇幅會(huì)放在后面兩個(gè)。
二、NSThread
2.1 NSThread簡(jiǎn)介
NSThread是經(jīng)過(guò)apple封裝的面向?qū)ο蟮模试S開發(fā)者直接以面向?qū)ο蟮乃枷雽?duì)線程進(jìn)行操作,沒(méi)一個(gè)NSThread對(duì)象就代表一條線程,但是開發(fā)者必須手動(dòng)管理線程的生命周期,這點(diǎn)是apple 不提倡的。如果開發(fā)者需要手動(dòng)管理線程時(shí)apple提出了以下幾點(diǎn)建議:
1、使用GCD和NSOperation
Writing thread-creation code manually is tedious and potentially error-prone and you should avoid it whenever possible. OS X and iOS provide implicit support for concurrency through other APIs
官方文檔中說(shuō),由于手動(dòng)管理線程很乏味,并且容易出錯(cuò),所以apple建議你使用apple自家提供的GCD和NSOperation來(lái)進(jìn)行多線程編程(apple心機(jī)婊,說(shuō)好的手動(dòng)管理線程的,怎么建議人家使用你的線程自動(dòng)管理API了呢)。
2、停止長(zhǎng)時(shí)間在idle狀態(tài)的線程
remember that threads consume precious system resources.Threads use a nontrivial amount of memory, some of it wired, so releasing an idle thread not only helps reduce your application’s memory footprint, it also frees up more physical memory for other system processes to use
由于線程會(huì)占據(jù)大量的系統(tǒng)資源,適當(dāng)?shù)闹v長(zhǎng)時(shí)間處于掛起狀態(tài)的線程停止,會(huì)釋放更多的CPU和內(nèi)存資源。
3、避免讓多條線程同時(shí)處理一個(gè)數(shù)據(jù)
The simplest and easiest way to avoid thread-related resource conflicts is to give each thread in your program its own copy of whatever data it needs.
使每條線程都操作自己需要操作的數(shù)據(jù),如果有必要的話將數(shù)據(jù)復(fù)制一份給另外一條線程處理。
...
如果你真的下定決心要自己管理線程,讓Threading Programming Guide幫助你吧。
2.2 NSThread使用:
apple提供了兩個(gè)方式來(lái)使用NSThread:
- 使用
detachNewThreadSelector:toTarget:withObject:
NSThread.detachNewThreadSelector(Selector("thread"), toTarget: self, withObject: nil)
- 使用
initWithTarget:selector:object:
方法創(chuàng)建線程,但是必須手動(dòng)啟動(dòng):
let thread = NSThread(target: self, selector:Selector("thread") , object: nil)
thread.start()
打印結(jié)果:
2016-03-23 16:51:51.238 Mutiple[9192:3103867] NSThread-(
"<NSThread: 0x7faf50653330>{number = 3, name = (null)}"
)
- 使用
performSelectorInBackground: withObject:
方法創(chuàng)建并自動(dòng)啟動(dòng)
遺憾的是swift無(wú)法使用這個(gè)方法,原因:
The performSelector: method and related selector-invoking methods are not imported in Swift because they are inherently unsafe.
apple認(rèn)為這個(gè)方法不是類型安全的。
2.3 NSThread幾個(gè)常用的方法:
//獲取主線程和當(dāng)前線程:
NSThread.mainThead()
NSThread.currentThread()
//退出,取消線程
NSThread.exit()
thread.cancel()
//判斷線程狀態(tài)
thread.executing//是否在運(yùn)行
thread.finished //是否結(jié)束
thread.cancelled //是否取消
//判斷是否為多線程
NSThread.isMultiThread()
//使用通知根據(jù)線程狀態(tài)做一些操作
NSDidBecomeSingleThreadedNotification//變?yōu)閱尉€程
NSThreadWillExitNotification//線程即將結(jié)束
NSWillBecomeMultiThreadedNotification//即將變?yōu)槎嗑€程
//暫停線程
三、GCD
GCD是apple提出的為提高多核效率的多線程實(shí)現(xiàn)方案,
This technology takes the thread management code you would normally write in your own applications and moves that code down to the system level. All you have to do is define the tasks you want to execute and add them to an appropriate dispatch queue. GCD takes care of creating the needed threads and of scheduling your tasks to run on those threads
它在系統(tǒng)層級(jí)幫助開發(fā)者維護(hù)線程的生命周期,包括線程的創(chuàng)建、休眠、銷毀等,開發(fā)者只需要關(guān)心需要實(shí)現(xiàn)的功能,將需要做的操作放到調(diào)度隊(duì)列(dispatch queue)中,系統(tǒng)會(huì)根據(jù)線程的情況自動(dòng)分配資源。
GCD引入了任務(wù)(task)和調(diào)度隊(duì)列(dispatch queue)的概念。
所謂的任務(wù),其實(shí)就是你要實(shí)現(xiàn)的功能和操作,即你要做什么;
調(diào)度隊(duì)列,是實(shí)現(xiàn)功能的方式,簡(jiǎn)單說(shuō)就是你想怎么做。
apple列舉了dispatch queue的許多優(yōu)點(diǎn):
- 提供了更簡(jiǎn)潔的實(shí)現(xiàn)多線程的接口,讓代碼更簡(jiǎn)潔
- 幫開發(fā)者管理線程的生命周期
- 異步隊(duì)列中不會(huì)造成死鎖
...
隊(duì)列中的所有任務(wù)都按照FIFO的順序執(zhí)行,GCD提供了三種隊(duì)列:
- 串行隊(duì)列
- 并行隊(duì)列
- 主隊(duì)列
3.1串行隊(duì)列
- 創(chuàng)建
使用dispatch_queue_create(label: UnsafePointer<Int8>, attr: dispatch_queue_attr_t)
方法創(chuàng)建隊(duì)列,來(lái)看看apple對(duì)方法中的兩個(gè)屬性的解釋:
label:
A string label to attach to the queue to uniquely identify it in debugging tools such as Instruments, sample, stackshots, and crash reports. Because applications, libraries, and frameworks can all create their own dispatch queues, a reverse-DNS naming style (com.example.myqueue) is recommended. This parameter is optional and can be NULL.
label是隊(duì)列的名稱,apple推薦使用com.example.myQueue
的規(guī)則來(lái)命名隊(duì)列,用于debug的時(shí)候追蹤隊(duì)列以便于調(diào)試,可以為空
attr:
In OS X v10.7 and later or iOS 4.3 and later, specify DISPATCH_QUEUE_SERIAL (or NULL) to create a serial queue or specify DISPATCH_QUEUE_CONCURRENT to create a concurrent queue. In earlier versions, you must specify NULL for this parameter.
attr用于指定是串行隊(duì)列還是并行隊(duì)列,如果是NULL
則默認(rèn)串行隊(duì)列
let serialQ1 = dispatch_queue_create("com.hah.serialQ1",DISPATCH_QUEUE_SERIAL)
或者
let serialQ2 = dispatch_queue_create("com.hah.serialQ2")
串行隊(duì)列中的任務(wù)會(huì)按照FIFO的順序依次執(zhí)行,在同步執(zhí)行下,系統(tǒng)不會(huì)開啟新的線程,默認(rèn)在主線程下執(zhí)行任務(wù),如果是在異步執(zhí)行下,系統(tǒng)會(huì)根據(jù)線程資源情況決定是否開啟新的線程。
GCD中可以通過(guò)串行隊(duì)列進(jìn)行鎖線程。
來(lái)看看同步情況下任務(wù)的執(zhí)行情況:
-
同步串行:任務(wù)一個(gè)接著一個(gè)執(zhí)行,不開啟新線程;
func syncSerial() { NSLog("=======>>>syncSerial<<<========") let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_SERIAL) dispatch_sync(q1) { () -> Void in NSLog("1-%@",NSThread.currentThread()); } dispatch_sync(q1) { () -> Void in NSLog("2-%@",NSThread.currentThread()); } dispatch_sync(q1) { () -> Void in NSLog("3-%@",NSThread.currentThread()); } dispatch_sync(q1) { () -> Void in NSLog("4-%@",NSThread.currentThread()); } dispatch_sync(q1) { () -> Void in NSLog("5-%@",NSThread.currentThread()]); } NSLog("=======>>>syncSerial<<<========") }
打印結(jié)果:
2016-03-23 13:38:29.521 Mutiple[8199:2165063] =======>>>syncSerial<<<========
2016-03-23 13:38:29.522 Mutiple[8199:2165063] 1-(
"<NSThread: 0x7f8418502320>{number = 1, name = main}"
)
2016-03-23 13:38:29.522 Mutiple[8199:2165063] 2-(
"<NSThread: 0x7f8418502320>{number = 1, name = main}"
)
2016-03-23 13:38:29.522 Mutiple[8199:2165063] 3-(
"<NSThread: 0x7f8418502320>{number = 1, name = main}"
)
2016-03-23 13:38:29.523 Mutiple[8199:2165063] 4-(
"<NSThread: 0x7f8418502320>{number = 1, name = main}"
)
2016-03-23 13:38:29.523 Mutiple[8199:2165063] 5-(
"<NSThread: 0x7f8418502320>{number = 1, name = main}"
)
2016-03-23 13:38:29.523 Mutiple[8199:2165063]
=======>>>syncSerial<<<========
- 異步串行:任務(wù)一個(gè)接著一個(gè)執(zhí)行,系統(tǒng)根據(jù)線程資源情況決定是否開啟新的線程;
func asyncSerail() { NSLog("=======>>>asyncSerail<<<========") let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_SERIAL) dispatch_async(q1) { () -> Void in NSLog("1-%@",NSThread.currentThread()); } dispatch_async(q1) { () -> Void in NSLog("2-%@",NSThread.currentThread()); } dispatch_async(q1) { () -> Void in NSLog("3-%@",NSThread.currentThread()); } dispatch_async(q1) { () -> Void in NSLog("4-%@",NSThread.currentThread()); } dispatch_async(q1) { () -> Void in NSLog("5-%@",NSThread.currentThread()); } NSLog("=======>>>asyncSerail<<<========") }
打印結(jié)果:
2016-03-23 13:04:26.713 Mutiple[7294:1959223] =======>>>asyncSerail<<<========
2016-03-23 13:04:26.714 Mutiple[7294:1959223] =======>>>asyncSerail<<<========
2016-03-23 13:04:26.715 Mutiple[7294:1959473] 1-(
"<NSThread: 0x7faebac3c740>{number = 3, name = (null)}"
)
2016-03-23 13:04:26.716 Mutiple[7294:1959473] 2-(
"<NSThread: 0x7faebac3c740>{number = 3, name = (null)}"
)
2016-03-23 13:04:26.716 Mutiple[7294:1959473] 3-(
"<NSThread: 0x7faebac3c740>{number = 3, name = (null)}"
)
2016-03-23 13:04:26.716 Mutiple[7294:1959473] 4-(
"<NSThread: 0x7faebac3c740>{number = 3, name = (null)}"
)
2016-03-23 13:04:26.716 Mutiple[7294:1959473] 5-(
"<NSThread: 0x7faebac3c740>{number = 3, name = (null)}"
)
從打印可以看出,系統(tǒng)開啟了新的線程,但是所有的任務(wù)都是在同一個(gè)線程里完成,避免在多個(gè)線程之間切換。
3.2 并行隊(duì)列
并行隊(duì)列,是全局隊(duì)列的一種,它能處理并發(fā)任務(wù)。apple提供了四個(gè)已經(jīng)創(chuàng)建好的并行隊(duì)列,又叫全局隊(duì)列(global queu),開發(fā)者只需要調(diào)用dispatch_get_global_queue( , )
就能得到一個(gè)并行隊(duì)列。并行隊(duì)列同樣按照FIFO的原則執(zhí)行任務(wù),它允許多個(gè)任務(wù)一起執(zhí)行,同時(shí)執(zhí)行的任務(wù)數(shù)量系統(tǒng)會(huì)根據(jù)情況決定。iOS5以后,apple也允許開發(fā)者自己創(chuàng)建并行隊(duì)列。
3.2.1 獲取全局隊(duì)列:
調(diào)用dispatch_get_global_queue(identifier: Int, flags: UInt)
方法獲取全局隊(duì)列:
兩個(gè)參數(shù):
identifier:
The quality of service you want to give to tasks executed using this queue. Quality-of-service helps determine the priority given to tasks executed by the queue. Queues that handle user-interactive or user-initiated tasks have a higher priority than tasks meant to run in the background.
identifier用于指定全局隊(duì)列的優(yōu)先級(jí),有4種類型:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
iOS 8以后可以通過(guò):
QOS_CLASS_USER_INTERACTIVE,
QOS_CLASS_USER_INITIATED,
QOS_CLASS_UTILITY
QOS_CLASS_BACKGROUND.
來(lái)設(shè)置優(yōu)先級(jí)。
從字面上就能看出,優(yōu)先級(jí)的高低順序?yàn)椋?code>DISPATCH_QUEUE_PRIORITY_HIGH>DISPATCH_QUEUE_PRIORITY_DEFAULT
>DISPATCH_QUEUE_PRIORITY_LOW
>DISPATCH_QUEUE_PRIORITY_BACKGROUND
需要特別說(shuō)一下的是,系統(tǒng)建議將有關(guān)磁盤的I/0操作放到DISPATCH_QUEUE_PRIORITY_BACKGROUND
優(yōu)先級(jí)的隊(duì)列中,防止占用過(guò)多的系統(tǒng)資源。
flags :
Flags that are reserved for future use. Always specify 0 for this parameter.
flag默認(rèn)是0
let highQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
//默認(rèn)優(yōu)先級(jí)也可以這么寫:let defaultQ = dispatch_get_global_queue(0, 0)
let defaultQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let lowQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
let bgQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)
我寫了段代碼模擬了下優(yōu)先級(jí)高的隊(duì)列先執(zhí)行的情況:
func queuePriority() {
let highQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
let defaultQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(defaultQ) { () -> Void in
NSLog("task3 start-%@",NSThread.currentThread());
}
dispatch_async(highQ) { () -> Void in
NSLog("task1 start-%@",NSThread.currentThread());
}
}
按理說(shuō),應(yīng)該是先執(zhí)行task1,因?yàn)樗鼉?yōu)先級(jí)比較高,但是log是這樣的:
2016-03-24 16:12:49.308 Mutiple[3815:2019744] task3 start-(
"<NSThread: 0x14de16b40>{number = 3, name = (null)}"
)
2016-03-24 16:12:49.308 Mutiple[3815:2019743] task1 start-(
"<NSThread: 0x14dd4e7b0>{number = 4, name = (null)}"
)
優(yōu)先級(jí)高的不先執(zhí)行了,why?
我用優(yōu)先級(jí)high和low做了實(shí)驗(yàn),發(fā)現(xiàn)high優(yōu)先級(jí)總是先執(zhí)行,但是default為什么就不行呢?這個(gè)暫時(shí)還不明白。
3.2.2 手動(dòng)創(chuàng)建并行隊(duì)列
接下來(lái)手動(dòng)創(chuàng)建一個(gè)并行隊(duì)列,查看線程的創(chuàng)建情況:
- 同步并行:任務(wù)一個(gè)接著一個(gè)執(zhí)行,系統(tǒng)默認(rèn)不開啟新線程;
func syncConcurrency() { NSLog("=======>>>syncConcurrency<<<========") let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_CONCURRENT) dispatch_sync(q1) { () -> Void in NSLog("1-%@",NSThread.currentThread()); } dispatch_sync(q1) { () -> Void in NSLog("2-%@",NSThread.currentThread()); } dispatch_sync(q1) { () -> Void in NSLog("3-%@",[NSThread .currentThread()]); } dispatch_sync(q1) { () -> Void in NSLog("4-%@",NSThread.currentThread()); } dispatch_sync(q1) { () -> Void in NSLog("5-%@",NSThread.currentThread()); } NSLog("=======>>>syncConcurrency<<<========") }
打印結(jié)果:
2016-03-23 13:40:16.968 Mutiple[8256:2181632] =======>>>syncConcurrency<<<========
2016-03-23 13:40:16.969 Mutiple[8256:2181632] 1-(
"<NSThread: 0x7fad99605d70>{number = 1, name = main}"
)
2016-03-23 13:40:16.970 Mutiple[8256:2181632] 2-(
"<NSThread: 0x7fad99605d70>{number = 1, name = main}"
)
2016-03-23 13:40:16.970 Mutiple[8256:2181632] 3-(
"<NSThread: 0x7fad99605d70>{number = 1, name = main}"
)
2016-03-23 13:40:16.970 Mutiple[8256:2181632] 4-(
"<NSThread: 0x7fad99605d70>{number = 1, name = main}"
)
2016-03-23 13:40:16.970 Mutiple[8256:2181632] 5-(
"<NSThread: 0x7fad99605d70>{number = 1, name = main}"
)
2016-03-23 13:40:16.970 Mutiple[8256:2181632] =======>>>syncConcurrency<<<========
從打印輸出可以看出,所有任務(wù)都是在主線程中完成,因?yàn)橥綀?zhí)行,即使開了多個(gè)線程,下一個(gè)的任務(wù)還是要等待當(dāng)前任務(wù)執(zhí)行完畢才能開始執(zhí)行,開多個(gè)線程之后造成資源浪費(fèi)。就像iOS多線程淺匯-原理篇中講到的,如果這個(gè)時(shí)候開了三條線程,系統(tǒng)就必須在三條線程之間切換,除此之外,每條線程都有自己的棧和寄存器,三條線程就有三組的棧和寄存器被復(fù)制和替換,這相對(duì)于只有一條線程來(lái)說(shuō),效率肯定是大大降低的。
- 異步并行:多個(gè)任務(wù)一起執(zhí)行,順序不固定,但是可以通過(guò)dispatch_group和NSOperation添加依賴控制任務(wù)執(zhí)行順序。
func asyncConcurrency() { NSLog("=======>>>asyncConcurrency<<<========") let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_CONCURRENT) dispatch_async(q1) { () -> Void in NSLog("1-%@",NSThread.currentThread()); } dispatch_async(q1) { () -> Void in NSLog("2-%@",NSThread.currentThread(); } dispatch_async(q1) { () -> Void in NSLog("3-%@",NSThread.currentThread()); } dispatch_async(q1) { () -> Void in NSLog("4-%@",NSThread.currentThread()); } dispatch_async(q1) { () -> Void in NSLog("5-%@",NSThread.currentThread()); } NSLog("=======>>>asyncConcurrency<<<========") }
打印結(jié)果:
2016-03-23 13:30:20.987 Mutiple[8107:2109033] =======>>>asyncConcurrency<<<========
2016-03-23 13:30:20.988 Mutiple[8107:2109033] =======>>>asyncConcurrency<<<========
2016-03-23 13:30:20.989 Mutiple[8107:2109139] 5-(
"<NSThread: 0x7fcca2546420>{number = 7, name = (null)}"
)
2016-03-23 13:30:20.989 Mutiple[8107:2109119] 3-(
"<NSThread: 0x7fcca2733150>{number = 4, name = (null)}"
)
2016-03-23 13:30:20.989 Mutiple[8107:2109093] 1-(
"<NSThread: 0x7fcca2732c90>{number = 3, name = (null)}"
)
2016-03-23 13:30:20.989 Mutiple[8107:2109118] 2-(
"<NSThread: 0x7fcca253ea80>{number = 5, name = (null)}"
)
2016-03-23 13:30:20.990 Mutiple[8107:2109138] 4-(
"<NSThread: 0x7fcca263a5e0>{number = 6, name = (null)}"
)
- 異步下創(chuàng)建多個(gè)串行隊(duì)列實(shí)現(xiàn)并行效果
func multiAsyncSerail() { NSLog("=======>>>asyncSerail<<<========") let q1 = dispatch_queue_create("q1",DISPATCH_QUEUE_SERIAL) dispatch_async(q1) { () -> Void in NSLog("1-%@",NSThread.currentThread()); } let q2 = dispatch_queue_create("q2",DISPATCH_QUEUE_SERIAL) dispatch_async(q2) { () -> Void in NSLog("2-%@",NSThread.currentThread()); } let q3 = dispatch_queue_create("q3",DISPATCH_QUEUE_SERIAL) dispatch_async(q3) { () -> Void in NSLog("3-%@",NSThread.currentThread()); } let q4 = dispatch_queue_create("q4",DISPATCH_QUEUE_SERIAL) dispatch_async(q4) { () -> Void in NSLog("4-%@",NSThread.currentThread()); } let q5 = dispatch_queue_create("q5",DISPATCH_QUEUE_SERIAL) dispatch_async(q5) { () -> Void in NSLog("5-%@",NSThread.currentThread()); } NSLog("=======>>>asyncSerail<<<========") }
打印結(jié)果:
2016-03-23 13:33:21.412 Mutiple[8150:2128078] =======>>>asyncSerail<<<========
2016-03-23 13:33:21.413 Mutiple[8150:2128078] =======>>>asyncSerail<<<========
2016-03-23 13:33:21.414 Mutiple[8150:2128320] 4-(
"<NSThread: 0x7fa520413420>{number = 7, name = (null)}"
)
2016-03-23 13:33:21.414 Mutiple[8150:2128308] 1-(
"<NSThread: 0x7fa52044b4d0>{number = 3, name = (null)}"
)
2016-03-23 13:33:21.414 Mutiple[8150:2128312] 3-(
"<NSThread: 0x7fa520432f10>{number = 5, name = (null)}"
)
2016-03-23 13:33:21.414 Mutiple[8150:2128321] 5-(
"<NSThread: 0x7fa52040beb0>{number = 6, name = (null)}"
)
2016-03-23 13:33:21.414 Mutiple[8150:2128311] 2-(
"<NSThread: 0x7fa52061ff60>{number = 4, name = (null)}"
)
小結(jié):
使用GCD實(shí)現(xiàn)多線程時(shí):
同步串行和同步并行:都會(huì)按照順序依次執(zhí)行任務(wù),且不會(huì)開啟新的線程。
異步串行:會(huì)開啟新的線程,但是系統(tǒng)默認(rèn)在同一線程內(nèi)按照任務(wù)順序依次執(zhí)行,因?yàn)檫@個(gè)時(shí)候開了多個(gè)線程也要順序執(zhí)行任務(wù),開線程只會(huì)造成資源浪費(fèi)
異步并行:開啟多個(gè)線程,有多個(gè)任務(wù)同時(shí)執(zhí)行,系統(tǒng)會(huì)根據(jù)每條線程的情況分配,執(zhí)行順序不固定。
在異步串行情況下,可以通過(guò)創(chuàng)建多個(gè)串行隊(duì)列,實(shí)現(xiàn)異步并行的功能,讓多個(gè)任務(wù)同時(shí)進(jìn)行。
3.3 主隊(duì)列
The main dispatch queue is a globally available serial queue that executes tasks on the application’s main thread.
主隊(duì)列是一個(gè)全局性的串行隊(duì)列,用于執(zhí)行app中主線程的任務(wù),對(duì)run loop起作用,將隸屬于run loop的事件源與隊(duì)列任務(wù)的執(zhí)行交織在一起(官方文檔翻譯過(guò)來(lái)的,太拗口了。。。)。主隊(duì)列是一個(gè)對(duì)于app來(lái)說(shuō)十分重要的同步點(diǎn),凡是與UI和用戶反饋相關(guān)的都必須交給主隊(duì)列完成。這個(gè)隊(duì)列是系統(tǒng)自動(dòng)創(chuàng)建的,開發(fā)者只需要獲取隊(duì)列,并執(zhí)行操作即可。
獲取主線程的方法:
let mainQueue = dispatch_get_main_queue()
最經(jīng)常用的:
let queue = dispatch_get_global_queue(0,0)
dispatch_async(queue,{ () -> Void in
//在這里做一些耗時(shí)的操作
dispatch_async(dispatch_get_main_queue(),{ () -> Void in
//刷新UI
})
})
3.4 隊(duì)列組(dispatch group)
Dispatch groups are a way to block a thread until one or more tasks finish executing.Groups provide a useful synchronization mechanism for code that depends on the completion of other tasks
使用隊(duì)列組能夠?qū)崿F(xiàn)讓一個(gè)或多個(gè)任務(wù)執(zhí)行完畢之后再執(zhí)行下一個(gè)任務(wù)。
Another way to use dispatch groups is as an alternative to thread joins.
使用隊(duì)列組將多個(gè)任務(wù)連接在一起。
用隊(duì)列組可以在特定的場(chǎng)景下實(shí)現(xiàn)一些特定的功能,十分有趣:
加入有三個(gè)任務(wù):taks1,task2,taks3。task1與task2沒(méi)有什么特殊關(guān)系,task3需要等待task1和task2結(jié)束之后才能開始執(zhí)行。
- 使用隊(duì)列組實(shí)現(xiàn)任務(wù)等待
func dispatchGroup() {
let group = dispatch_group_create()
let queue1 = dispatch_get_global_queue(0, 0)
let queue2 = dispatch_get_global_queue(0, 0)
let queue3 = dispatch_get_global_queue(0, 0)
dispatch_group_async(group, queue1) { () -> Void in
for i in 0..<3 {
NSLog("task1 - %d",i)
}
}
dispatch_group_async(group, queue2) { () -> Void in
for i in 0..<3 {
NSLog("task2 - %d",i)
}
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
NSLog("wait completed")
dispatch_group_async(group, queue3) { () -> Void in
for i in 0..<3 {
NSLog("task3 - %d",i)
}
}
}
log輸出:
2016-03-24 22:23:25.717 2016-03-24 22:42:42.465 MultipleThradDemo[49112:4109966] task2 - 0
2016-03-24 22:42:42.465 MultipleThradDemo[49112:4109967] task1 - 0
2016-03-24 22:42:42.467 MultipleThradDemo[49112:4109966] task2 - 1
2016-03-24 22:42:42.467 MultipleThradDemo[49112:4109967] task1 - 1
2016-03-24 22:42:42.468 MultipleThradDemo[49112:4109966] task2 - 2
2016-03-24 22:42:42.468 MultipleThradDemo[49112:4109967] task1 - 2
2016-03-24 22:42:42.468 MultipleThradDemo[49112:4109901] wait completed
2016-03-24 22:42:42.469 MultipleThradDemo[49112:4109967] task3 - 0
2016-03-24 22:42:42.469 MultipleThradDemo[49112:4109967] task3 - 1
2016-03-24 22:42:42.469 MultipleThradDemo[49112:4109967] task3 - 2
dispatch_group_wait(group: dispatch_group_t, timeout:dispatch_time_t)
:括號(hào)里面的第一個(gè)參數(shù)group
為等待的隊(duì)列組,第二個(gè)timeout
為等待的時(shí)間
- 使用隊(duì)列組的通知功能實(shí)現(xiàn)上述場(chǎng)景:
func groupNotify() {
let group = dispatch_group_create()
let queue1 = dispatch_get_global_queue(0, 0)
let queue2 = dispatch_get_global_queue(0, 0)
let queue3 = dispatch_get_global_queue(0, 0)
dispatch_group_async(group, queue1) { () -> Void in
for i in 0..<3 {
NSLog("task1 - %d",i)
}
}
dispatch_group_async(group, queue2) { () -> Void in
for i in 0..<3 {
NSLog("task2 - %d",i)
}
}
dispatch_group_notify(group, queue3) { () -> Void in
NSLog("task1、 task2 completed")
for i in 0..<3 {
NSLog("task3 - %d",i)
}
}
}
log輸出:
2016-03-24 23:05:29.010 MultipleThradDemo[49493:4127151] task2 - 0
2016-03-24 23:05:29.010 MultipleThradDemo[49493:4127148] task1 - 0
2016-03-24 23:05:29.011 MultipleThradDemo[49493:4127148] task1 - 1
2016-03-24 23:05:29.011 MultipleThradDemo[49493:4127151] task2 - 1
2016-03-24 23:05:29.011 MultipleThradDemo[49493:4127148] task1 - 2
2016-03-24 23:05:29.011 MultipleThradDemo[49493:4127151] task2 - 2
2016-03-24 23:05:29.012 MultipleThradDemo[49493:4127151] task1、 task2 completed
2016-03-24 23:05:29.012 MultipleThradDemo[49493:4127151] task3 - 0
2016-03-24 23:05:29.012 MultipleThradDemo[49493:4127151] task3 - 1
2016-03-24 23:05:29.012 MultipleThradDemo[49493:4127151] task3 - 2
3.5 GCD的線程安全
盡管GCD本身就是線程安全的,但是為了更好的使用GCD,apple還是給了幾個(gè)建議:
Do not call the dispatch_sync function from a task that is executing on the same queue that you pass to your function call
在本隊(duì)列調(diào)用dispatch_sync
,再將本隊(duì)列傳入同步方法會(huì)造成死鎖。
我想apple 的意思應(yīng)該是這樣的:
func deadLock() {
//let q = dispatch_get_global_queue(0, 0)
let q = dispatch_queue_create("come.multiThread.serialQ", DISPATCH_QUEUE_SERIAL)
dispatch_async(q) { () -> Void in
NSLog("1 start-%@",[NSThread .currentThread()]);
dispatch_sync(q, { () -> Void in
NSLog("2 start-%@",[NSThread .currentThread()]);
})
NSLog("3 start-%@",[NSThread .currentThread()]);
}
NSLog("4 start-%@",[NSThread .currentThread()]);
}
但是我發(fā)現(xiàn)會(huì)不會(huì)造成死鎖和當(dāng)前隊(duì)列是串行還是并行有關(guān)。
如果是串行隊(duì)列,同步執(zhí)行會(huì)阻塞線程,而且串行隊(duì)列一次只能執(zhí)行一個(gè)任務(wù),q里面等待的任務(wù)與同步阻塞的任務(wù)是同一個(gè)任務(wù),造成死鎖。
如果是并行隊(duì)列,一次能執(zhí)行多個(gè)任務(wù),即使同步阻塞了線程,由于可以多個(gè)任務(wù)一起執(zhí)行,q里面沒(méi)有任務(wù)等待,不會(huì)造成死鎖。
stackoverflow上類似問(wèn)題的回答:
Using
dispatch_sync()
to enqueue a Block to the serial queue on which you're already running is guaranteed to deadlock.Usingdispatch_sync()
to enqueue a Block to the concurrent queue on which you're already running is quite likely to deadlock.
派發(fā)同步任務(wù)到在正在執(zhí)行的串行隊(duì)列肯定會(huì)造成死鎖;派發(fā)到并行隊(duì)列也有很大的可能造成死鎖。所以上面的例子中,并行隊(duì)列雖然沒(méi)有造成死鎖,還是有風(fēng)險(xiǎn)的。
note:往主線程派發(fā)同步任務(wù)也會(huì)造成死鎖
func mainThreadLock() {
let main = dispatch_get_main_queue()
NSLog("start-%@",[NSThread .currentThread()]);
dispatch_sync(main) { () -> Void in
NSLog("1 -%@",[NSThread .currentThread()]);
}
NSLog("end-%@",[NSThread .currentThread()]);
}
打印輸出:
2016-03-25 10:15:46.638 Mutiple[15813:5558093] start-(
"<NSThread: 0x7fae226029d0>{number = 1, name = main}"
)
同步任務(wù)與主線程任務(wù)相互等待,造成死鎖。
Avoid taking locks from the tasks you submit to a dispatch queue.If you need to synchronize parts of your code, use a serial dispatch queue instead of a lock.
盡量不用自己鎖線程,否則得到的結(jié)果可能會(huì)十分出乎你的意料。使用GCD實(shí)現(xiàn)多線程時(shí),apple建議用串行隊(duì)列替代線程鎖。
3.6 GCD的其他常用方法
3.6.1 dispatch_group_enter
dispatch_group_enter()
和dispatch_group_leave()
是用于進(jìn)入group和退出group的指示方法,是用于手動(dòng)管理group的block塊的方法,下面兩段代碼的功能是相同的:
let group = dispatch_group_create()
let queue = dispatch_get_global_queue(0,0)
//實(shí)現(xiàn)異步操作
dispatch_group_enter(group)
dispatch_async(queue,^{
//異步操作
dispatch_group_leave(group)
})
//等價(jià)于
dispatch_group_async(group,queue,^{
//異步操作
})
3.6.1 dispatch semaphore
dispatch semaphore 信號(hào)量用于控制同時(shí)訪問(wèn)同一個(gè)資源的任務(wù)數(shù)量。當(dāng)一個(gè)并行任務(wù)中需要對(duì)資源數(shù)據(jù)做更小部分的控制或者多個(gè)線程存在競(jìng)爭(zhēng)的時(shí)候,信號(hào)量就變得十分有用。
信號(hào)量主要有三個(gè)方法:
/*!
*@function
*創(chuàng)建信號(hào)量
*
*@description
*value=0:兩個(gè)線程需要同時(shí)完成
*value<0:返回NULL
*
*@param value
*用于指定開始的時(shí)候的信號(hào)量的個(gè)數(shù)
*/
func dispatch_semaphore_create(value:Int) -> dispatch_semaphore_t!
/*!
*@function
*等待信號(hào),并使得create方法中的value值-1
*
*@description
*返回值>=0時(shí)使得信號(hào)量減一并繼續(xù)執(zhí)行以下代碼,<0會(huì)等待信號(hào)量發(fā)送后再返回返回值
*
*@param dsema
*等待的信號(hào)
*
*@param timeout
*等待的時(shí)間
*@result
*0:成功,非零:等待
*/
func dispatch_semaphore_wait(dsema: dispatch_semaphore_t,timeout: dispatch_time_t) -> Int
/*!
*@function
*使得信號(hào)量增加
*
*@description
*如果要返回的值<0,會(huì)先喚醒一個(gè)等待線程再返回值
*
*@param dsema
*等待的信號(hào)
*
*@result
*0:未能喚醒線程,非零:?jiǎn)拘丫€程
*/
func dispatch_semaphore_signal(dsema:dispatch_semaphore_t) -> Int
看一個(gè)例子:
func changeMutableArray() {
var arr = [Int]()
let queue1 = dispatch_get_global_queue(0, 0)
for i in 0...3 {
dispatch_async(queue1) { () -> Void in
NSLog("task2 - %d",i)
arr.append(i)
NSLog("\(arr)")
}
}
}
在兩個(gè)異步線程中對(duì)一個(gè)可變數(shù)組做添加元素操作,由于可變數(shù)組不支持多線程操作,當(dāng)兩個(gè)線程同時(shí)訪問(wèn)它時(shí)就會(huì)造成數(shù)據(jù)混亂,我得出了這樣的打印:
2016-03-28 16:44:34.193 Mutiple[5669:1717543] task2 - 1
2016-03-28 16:44:34.193 Mutiple[5669:1717545] task2 - 2
2016-03-28 16:44:34.193 Mutiple[5669:1717544] task2 - 0
2016-03-28 16:44:34.193 Mutiple[5669:1717554] task2 - 3
2016-03-28 16:44:34.194 Mutiple[5669:1717544] [1, 2, 0]
2016-03-28 16:44:34.194 Mutiple[5669:1717554] [1, 2, 0, 3]
2016-03-28 16:44:34.194 Mutiple[5669:1717545] [1, 2, 0]
2016-03-28 16:44:34.194 Mutiple[5669:1717543] [1, 2]
加上了信號(hào)量的保護(hù)之后,代碼是這樣的
func changeMutableArray() {
var arr = [Int]()
let queue1 = dispatch_get_global_queue(0, 0)
let semaphore = dispatch_semaphore_create(1)
for i in 0...3 {
dispatch_async(queue1) { () -> Void in
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
NSLog("task2 - %d",i)
arr.append(i)
NSLog("\(arr)")
dispatch_semaphore_signal(semaphore)
}
}
}
剛開始創(chuàng)建的時(shí)候定義了初始的計(jì)數(shù)器的值為1,dispatch_semaphore_wait()
在執(zhí)行前會(huì)先判斷當(dāng)前的counter值,如果counter值大于等于1,會(huì)往下執(zhí)行代碼,并且使得counter值減1,如果counter的值小于1,等待dispatch_semaphore_signal()
方法執(zhí)行,該方法會(huì)使得counter加1,當(dāng)counter大于等于1 wait方法才會(huì)繼續(xù)執(zhí)行以下的代碼,
打印出來(lái)的log 信息是這樣的:
2016-03-28 16:46:54.272 Mutiple[5697:1733417] task2 - 0
2016-03-28 16:46:54.273 Mutiple[5697:1733417] [0]
2016-03-28 16:46:54.273 Mutiple[5697:1733416] task2 - 1
2016-03-28 16:46:54.273 Mutiple[5697:1733416] [0, 1]
2016-03-28 16:46:54.274 Mutiple[5697:1733440] task2 - 2
2016-03-28 16:46:54.274 Mutiple[5697:1733440] [0, 1, 2]
2016-03-28 16:46:54.274 Mutiple[5697:1733463] task2 - 3
2016-03-28 16:46:54.274 Mutiple[5697:1733463] [0, 1, 2, 3]
可以看出,信號(hào)量在競(jìng)爭(zhēng)添加下保證了往數(shù)組添加元素的安全性。
除此之外信號(hào)量還可以這樣用
let result = dispatch_semaphore_wait(dsema,DISPATCH_TIME_FORVEER)
if result > 0 {
//做異步操作
}else{
//等待signal方法增加counter值
}
NOTE:如果wait和signal不匹配出現(xiàn)可能會(huì)崩潰。
3.6.2 dispatch barrier
dispatch barrier允許開發(fā)者在一個(gè)并行隊(duì)列中創(chuàng)造一個(gè)同步點(diǎn),其實(shí)也是用于在競(jìng)爭(zhēng)條件下資源的保護(hù),防止同一資源同時(shí)被使用。在并行隊(duì)列中,barrier中的代碼會(huì)等到該隊(duì)列的所有并行條件結(jié)束之后才會(huì)單獨(dú)執(zhí)行barrier。
barrier兩個(gè)常用方法:
/*
*@discussion
*將barrier代碼塊提交到隊(duì)列中,然后馬上返回。
*等待隊(duì)列執(zhí)行完barrier前面的所有代碼之后,barrier里面的代碼會(huì)單獨(dú)執(zhí)行,
*等到barrier代碼執(zhí)行完畢,隊(duì)列繼續(xù)往下執(zhí)行。
*/
func dispatch_barrier_async(queu: dispatch_queue_t, block: dispatch_block_t)
note:這個(gè)隊(duì)列應(yīng)該使用自己創(chuàng)建的隊(duì)列,如果傳入的是一個(gè)串行隊(duì)列或者全局隊(duì)列,那么這個(gè)方法和dsipatch_async
的功能是一樣的。
/*
*@discussion
*提交barrier到隊(duì)列,等待barrier的block執(zhí)行完畢再返回。
*這個(gè)方法會(huì)等待block執(zhí)行完畢才會(huì)返回。
*同dispatch_barrier_async,隊(duì)列需要使用自己創(chuàng)建的私人隊(duì)列。
*/
func dispatch_barrier_sync(queue:dispatch_queue_t, block: dispatch_block_t)
```
**note: 在當(dāng)前隊(duì)列中調(diào)用此方法會(huì)造成死鎖, 系統(tǒng)傾向于當(dāng)前線程中(而不是再開一條線程)調(diào)用此方法使性能最優(yōu)。**
`dispatch_barrier_async`傳入自己創(chuàng)建的并行隊(duì)列時(shí),會(huì)阻塞**當(dāng)前隊(duì)列**執(zhí)行,而不阻塞當(dāng)前線程。
`dispatch_barrier_sync `傳入自己創(chuàng)建的并行隊(duì)列時(shí),會(huì)阻塞當(dāng)前線程,所有在正在執(zhí)行的隊(duì)列中調(diào)用次方法會(huì)使隊(duì)列任務(wù)相互等待,造成死鎖。
用代碼試驗(yàn)一下:
```
func asyncBarrier() {
let queue = dispatch_queue_create("com.multiThread.barrierQ", DISPATCH_QUEUE_CONCURRENT)
dispatch_async(queue) { () -> Void in
for i in 0...2 {
NSLog("task1 - %d",i)
}
}
dispatch_barrier_async(queue) { () -> Void in
NSLog("=====>>>barrier1 start <<<======")
for i in 0...2 {
NSLog("task2 - %d",i)
}
NSLog("=====>>>barrier1 completed <<<======")
}
dispatch_async(queue) { () -> Void in
for i in 0...2 {
NSLog("task4 - %d",i)
}
}
}
```
打印輸出:
>2016-03-29 11:11:33.496 Mutiple[7756:2543177] task1 - 0
2016-03-29 11:11:33.496 Mutiple[7756:2543177] task1 - 1
2016-03-29 11:11:33.496 Mutiple[7756:2543177] task1 - 2
2016-03-29 11:11:33.497 Mutiple[7756:2543177] =====>>>barrier1 start <<<======
2016-03-29 11:11:33.497 Mutiple[7756:2543177] task2 - 0
2016-03-29 11:11:33.497 Mutiple[7756:2543177] task2 - 1
2016-03-29 11:11:33.497 Mutiple[7756:2543177] task2 - 2
2016-03-29 11:11:33.497 Mutiple[7756:2543177] =====>>>barrier1 completed <<<======
2016-03-29 11:11:33.497 Mutiple[7756:2543177] task4 - 0
2016-03-29 11:11:33.497 Mutiple[7756:2543177] task4 - 1
2016-03-29 11:11:33.498 Mutiple[7756:2543177] task4 - 2
造成死鎖的情況:
```
func syncBarrier() {
let queue = dispatch_queue_create("com.multiThread.barrierQ", DISPATCH_QUEUE_CONCURRENT)
dispatch_async(queue) { () -> Void in
NSLog("sync barrier start")
dispatch_barrier_sync(queue) { () -> Void in
NSLog("=====>>>barrier1 start <<<======")
for i in 0...2 {
NSLog("task2 - %d",i)
}
NSLog("=====>>>barrier1 completed <<<======")
}
NSLog("sync barrier completed")
}
}
```
只有一條打印信息:
>2016-03-29 11:15:03.510 Mutiple[7851:2563610] sync barrier start
####3.6.3 dispatch source
dispatch source是一個(gè)監(jiān)視某些類型事件的對(duì)象。當(dāng)這些事件發(fā)生時(shí),它自動(dòng)將一個(gè)block放入一個(gè)dispatch queue的執(zhí)行例程中。
GCD支持以下的dispatch source類型:
* *Timer dispatch sources*
* *Signal dispatch sources*
* *Descriptor sources*
* *Process dispatch sources*
* *Mach port dispatch sources*
* *Custom dispatch sources*
dispatch sources 用一些異步回調(diào)方法處理系統(tǒng)相關(guān)的事件。不像之前手動(dòng)的將任務(wù)提交到dispatch queue,dispatch source為app提供了持續(xù)的事件源,在你取消它之前它都會(huì)一直連接這dispatch queue,等待著事件的觸發(fā)(有點(diǎn)像通知的感覺(jué))
* 創(chuàng)建source
```
//dispatch source 一直處于掛起狀態(tài),創(chuàng)建source,配置了handler和context之后,需要調(diào)用dispatch_resume()方法
public func dispatch_source_create(type: dispatch_source_type_t,
_ handle: UInt, _ mask: Uint,
_ queue: dispatch_queue_t!) -> dispatch_source_t!
```
* 配置事件
```
dispatch_source_set_event_handle(source:dispatch_source_t, handler: dispatch_block_t!)
```
文檔中的示例代碼:
```
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
myDescriptor, 0, myQueue);
dispatch_source_set_event_handler(source, ^{
// Get some data from the source variable, which is captured
// from the parent context.
size_t estimated = dispatch_source_get_data(source);
// Continue reading the descriptor...
});
dispatch_resume(source);
```
##四、NSOeration
NSOperation 是apple 提供的面向?qū)ο蟮亩嗑€程實(shí)現(xiàn)方案。`NSOperation`和`NSOperationQueue`分別對(duì)應(yīng)GCD中的task和queue
`NSOperation`是一個(gè)抽象類,不能直接創(chuàng)建對(duì)象封裝任務(wù),可以使用它的兩個(gè)子類`NSInvocationOperation`和`NSBlockOperation`或者自己創(chuàng)建繼承自NSOperation的類對(duì)象封裝任務(wù)。
###4.1 NSInvocation的使用:
```
NSInvocation *invocation = [[NSInvocation alloc] initWithTarget:aTarget
selector:@selector(aSelector)
object:nil];
[invocation start];
```
*NOTE:Swift中認(rèn)為`NSInvocationOperation`是非類型安全的,所有不能使用*
###4.2 NSBlockOperation
NSBlockOperation可以并行執(zhí)行一個(gè)或多個(gè)任務(wù),開發(fā)者只需要將任務(wù)放到block里面。在創(chuàng)建NSBlockOperation對(duì)象的時(shí)候,你就要應(yīng)該初始化至少一個(gè)block。這個(gè)operation會(huì)等到所有的block都執(zhí)行完畢才會(huì)結(jié)束。`finished`屬性是一個(gè)布爾值,可以監(jiān)測(cè)blockOperation是否執(zhí)行完畢。將一組的任務(wù)添加到block,然后監(jiān)測(cè)`finished`就能追蹤一組任務(wù)的完成情況。
基本用法
```
func blockOperation() {
let theOP = NSBlockOperation { () -> Void in
for i in 0...2 {
NSLog("task1 - %d",i)
}
}
theOP.addExecutionBlock { () -> Void in
for i in 0...2 {
NSLog("task2 - %d",i)
}
}
//不調(diào)用start方法不會(huì)開始執(zhí)行
theOP.start()
}
```
###4.3 自定義operation子類
當(dāng)系統(tǒng)提供的兩個(gè)NSOperation的子類無(wú)法滿足咱們的需求的時(shí)候,咱們就要?jiǎng)?chuàng)建繼承自NSOperation的對(duì)象來(lái)實(shí)現(xiàn)更多更復(fù)雜的操作。
自己創(chuàng)建的子類對(duì)象必要實(shí)現(xiàn)兩個(gè)方法:
* 自定義的初始化方法
* 重寫`main`方法。在這個(gè)方法里面所需要的操作,一般會(huì)建立自己的自動(dòng)釋放池,原因:異步執(zhí)行的時(shí)候無(wú)法訪問(wèn)主線程的釋放池,不會(huì)及時(shí)釋放,可能造成資源浪費(fèi)
```
@interface MyNonConcurrentOperation : NSOperation
@property (strong) id myData;
-(id)initWithData:(id)data;
@end
@implementation MyNonConcurrentOperation
- (id)initWithData:(id)data {
if (self = [super init])
myData = data;
return self;
}
-(void)main {
@autoreleasepool{
//執(zhí)行操作
}
}
@end
```
###4.4 讓NSOperation實(shí)現(xiàn)并行的幾個(gè)方法
operation對(duì)象默認(rèn)以同步方式執(zhí)行任務(wù),哪條線程調(diào)用了start方法,就會(huì)在哪條線程上執(zhí)行任務(wù)。盡管大多數(shù)operation是異步運(yùn)行的,operation隊(duì)列只提供非并行操作的線程。如果開發(fā)者要實(shí)現(xiàn)真正的并行操作,可以通過(guò)重寫以下幾個(gè)方法:
* start(Required)
所有的并發(fā)操作都必須重寫這個(gè)方法,用自己的實(shí)現(xiàn)方式替代默認(rèn)的。執(zhí)行操作時(shí),調(diào)用`start`方法,作為執(zhí)行任務(wù)的起始點(diǎn),在任何時(shí)候都不應(yīng)該調(diào)用 `super`的`start`方法
* main(Optional)
主要用于實(shí)現(xiàn)與operation對(duì)象相關(guān)的操作,盡管你可以在`start`方法中執(zhí)行任務(wù),但是將執(zhí)行任務(wù)的代碼放到這個(gè)方法中,會(huì)使得配置代碼與任務(wù)代碼分離得更加清晰些。
* isExcuting & isFinished(Required)
用于向外部報(bào)告任務(wù)的執(zhí)行情況,實(shí)現(xiàn)這兩個(gè)方法時(shí)需要保證線程安全。
* isConcurrent(Required)
標(biāo)記一個(gè)operation是否是并行操作,只需要重寫這個(gè)方法 返回`YES`。
簡(jiǎn)單實(shí)現(xiàn)功能:
```
@interface MyOperation : NSOperation {
BOOL executing;
BOOL finished;
}
- (void)completeOperation;
@end
@implementation MyOperation
- (id)init {
self = [super init];
if (self) {
executing = NO;
finished = NO;
}
return self;
}
- (void)start {
// Always check for cancellation before launching the task.
if ([self isCancelled])
{
// Must move the operation to the finished state if it is canceled.
[self willChangeValueForKey:@"isFinished"];
finished = YES;
[self didChangeValueForKey:@"isFinished"];
return;
}
// If the operation is not canceled, begin executing the task.
[self willChangeValueForKey:@"isExecuting"];
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
executing = YES;
[self didChangeValueForKey:@"isExecuting"];
}
- (void)main {
@autoreleasepool {
[self completeOperation];
}
}
- (void)completeOperation {
[self willChangeValueForKey:@"isFinished"];
[self willChangeValueForKey:@"isExecuting"];
executing = NO;
finished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
- (BOOL)isConcurrent {
return YES;
}
- (BOOL)isExecuting {
return executing;
}
- (BOOL)isFinished {
return finished;
}
@end
```
###4.5 NSOpreationQueue
同GCD種的dispatch queue一樣,operation queue由系統(tǒng)控制,開發(fā)者只需要把任務(wù)提交給它,系統(tǒng)會(huì)自動(dòng)判斷決定同時(shí)運(yùn)行的操作個(gè)數(shù),多創(chuàng)建一個(gè)queue并不意味著你可以同時(shí)執(zhí)行更多的操作。
```
//創(chuàng)建一個(gè)operation queue
let OPQueue = NSOperationQueue()
//添加操作
[aQueue addOperation:anOp]; // Add a single operation
//添加操作組,并決定是否等待組內(nèi)操作完成再繼續(xù)往下執(zhí)行
[aQueue addOperations:anArrayOfOps waitUntilFinished:NO]; // Add multiple operations
//直接添加操作到block中
[aQueue addOperationWithBlock:^{
/* Do something. */
}];
```
> **Important**: *Never modify an operation object after it has been added to a queue. While waiting in a queue, the operation could start executing at any time, so changing its dependencies or the data it contains could have adverse effects. If you want to know the status of an operation, you can use the methods of the NSOperation class to determine if the operation is running, waiting to run, or already finished.*
apple建議開發(fā)者在operation對(duì)象被提交到隊(duì)列后就不要去更改operation對(duì)象了,因?yàn)樗S時(shí)會(huì)開始執(zhí)行,如果需要知道operation的狀態(tài),通過(guò)NSOperation的方法來(lái)確定狀態(tài)。
使用操作隊(duì)列的幾個(gè)方法實(shí)現(xiàn)任務(wù)等待:
```
func operationQueue() {
let OP1 = NSBlockOperation { () -> Void in
for i in 0...2 {
NSLog("task1 - %d",i)
}
}
//OP2執(zhí)行完成之后的操作
OP1.completionBlock = { () in
NSLog("===>>task1 completed<<===")
}
let OP2 = NSBlockOperation { () -> Void in
for i in 0...2 {
NSLog("task2 - %d",i)
}
}
//添加依賴,OP1執(zhí)行完畢之后才會(huì)執(zhí)行OP2
OP2.addDependency(OP1)
//OP2執(zhí)行完成之后的操作
OP2.completionBlock = { () in
NSLog("===>>task2 completed<<===")
}
let OP3 = NSBlockOperation { () -> Void in
for i in 0...2 {
NSLog("task3 - %d",i)
}
}
let opQ = NSOperationQueue()
opQ.addOperations([OP1,OP2], waitUntilFinished: true)
opQ.addOperation(OP3)
}
```
log信息:
>2016-03-30 17:05:43.303 Mutiple[2363:858459] task1 - 0
2016-03-30 17:05:43.303 Mutiple[2363:858459] task1 - 1
2016-03-30 17:05:43.304 Mutiple[2363:858459] task1 - 2
2016-03-30 17:05:43.304 Mutiple[2363:858459] ===>>task1 completed<<===
2016-03-30 17:05:43.304 Mutiple[2363:858460] task2 - 0
2016-03-30 17:05:43.305 Mutiple[2363:858460] task2 - 1
2016-03-30 17:05:43.305 Mutiple[2363:858460] task2 - 2
2016-03-30 17:05:43.305 Mutiple[2363:858460] ===>>task2 completed<<===
2016-03-30 17:05:43.305 Mutiple[2363:858459] task3 - 0
2016-03-30 17:05:43.307 Mutiple[2363:858459] task3 - 1
2016-03-30 17:05:43.308 Mutiple[2363:858459] task3 - 2
operation queue通過(guò)`setMaxConcurrentOperationCount:1`來(lái)實(shí)現(xiàn)串行隊(duì)列的功能,但是執(zhí)行的順序會(huì)根據(jù)其他因素來(lái)確定,這一點(diǎn)與串行隊(duì)列又有點(diǎn)不太一樣。
>If the execution order of your operation objects is important to you, you should use dependencies to establish that order before adding your operations to a queue
如果需要自己決定執(zhí)行順序,使用依賴來(lái)實(shí)現(xiàn)。
初次之外,operation que還可以暫停operation,通過(guò)一個(gè)布爾值`suspended`的真和假來(lái)決定是否讓operation掛起。
當(dāng)`suspended = YES`,隊(duì)列不會(huì)開啟新的operation,但是正在執(zhí)行的操作會(huì)繼續(xù)運(yùn)行不會(huì)被暫停也不會(huì)被取消。這個(gè)時(shí)候可以繼續(xù)往隊(duì)列中添加操作,這些操作同樣會(huì)處于掛起狀態(tài),直到`suspended = NO`。操作只會(huì)在結(jié)束執(zhí)行的時(shí)候被移除,可以通過(guò)KVO觀察這個(gè)值的變化做相應(yīng)的操作。
> [閱讀原文](http://nucleardev.com/iOS多線程淺匯-實(shí)踐篇/)