iOS-多執行緒程式設計學習之GCD——序列佇列和併發佇列(五)
Grand Central Dispatch(GCD)有很多部分構成,例如有很好的語言特性,執行庫,還提供了系統的、高效的方式來支援具有多核處理器的iOS和OS X裝置進行併發事件處理。
BSD子系統,CoreFoundation和Cocoa APIs都已經使用這個增強特性來進行擴充套件了,因為它可以使得你的系統和應用執行的更快,更有效率,響應更及時。GCD是執行在系統級別的,他可以更好的滿足應用執行時的需求,更合理排程有限的系統資源。
GCD並不是被限制只能在系統級別應用上使用的,我們可以在更高層的應用上也使用它。但是一般情況下我們還可以考慮Cocoa中是否有類似的物件可以實現相似的功能,例如NSOperation,它也提供了很簡單便捷的方式來進行併發任務處理【
一、GCD與ARC
當使用Objective-C
來構建應用時,所有的dispatch obeject
都是Objective-C
型別的物件。所以當在應用中開啟了ARC(自動引用計數)時,這些dispatch obeject
也會想OC的物件一樣被自動的傳送retain
和release
訊息。當ARC被禁用時(也就是進行MRC手動記憶體管理時),可以通過使用dispatch_retain
和dispatch_release
來實現類似OC物件的retain和release操作。
二、建立任務佇列
GCD中的佇列主要分兩種。
- 併發佇列:允許多個任務同時執行。
- 序列佇列:任務按順序依次執行。
系統預設有一個和主執行緒繫結的序列佇列,還有一個全域性的併發佇列。這兩個佇列都可以通過響應的方法來獲得。
2.1 獲得主執行緒的序列佇列
呼叫這個方法獲取主執行緒佇列時,該佇列是由主執行緒來自動建立的。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
2.2 獲得全域性的併發佇列
獲取全域性併發佇列的方法中有兩個引數,第二個引數暫時沒有使用,預設傳值0就好。第一個引數表示佇列的優先順序,其中優先順序主要分為4種。
- DISPATCH_QUEUE_PRIORITY_HIGH 2,優先順序高
- DISPATCH_QUEUE_PRIORITY_DEFAULT 0,預設優先順序,中
- DISPATCH_QUEUE_PRIORITY_LOW -2,優先順序低
- DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN,後臺模式,優先順序最低
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2.3 建立自定義的佇列
建立自定義的佇列主要有兩種,一種是序列佇列,一種是並行佇列。在建立自定義佇列時會需要傳兩個引數,第一個為佇列的名稱,第二個為表示佇列型別的常量(序列還是並行)。
DISPATCH_QUEUE_SERIAL //序列佇列
DISPATCH_QUEUE_CONCURRENT //並行佇列
程式碼也很簡單。
//序列佇列
dispatch_queue_t serialQueue = dispatch_queue_create("com.lysongzi.serial", DISPATCH_QUEUE_SERIAL);
//並行佇列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.lysongzi.concurrent", DISPATCH_QUEUE_CONCURRENT);
三、同步和非同步
當我們建立了佇列之後,我們需要把任務新增到佇列中,並指定以同步還是非同步的方式執行新增到佇列中的任務。
同步,其會在當前執行緒立即執行新增的任務(無論是序列還是並行佇列都如此)。
非同步,其會新建立一個新的執行緒來執行任務。而非同步對於序列和並行佇列的又不一樣的意義的。
對於非同步執行的序列佇列的話,新新增的多個任務會在新建立的執行緒中依次執行,即一個執行完在執行另一個任務,有點類似同步執行的樣子(其區別可以看做是同步不會建立新的執行緒,而非同步會建立新的執行緒,且只建立一個)。我們可以看一下例子。
-(void)testSerialQueue
{
//建立序列佇列,傳入引數為DISPATCH_QUEUE_SERIAL
self.serialQueue = dispatch_queue_create("com.lysongzi.serial", DISPATCH_QUEUE_SERIAL);
//同步執行佇列中的任務,會立即在當前執行緒執行該任務
dispatch_sync(self.serialQueue, ^{
NSLog(@"這裡是同步執行的序列佇列01。===> %@", [NSThread currentThread]);
});
//非同步執行任務,會新開一個執行緒,多個任務按順序執行
dispatch_async(self.serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"序列01 ===> %d ===> %@", i, [NSThread currentThread]);
}
});
dispatch_sync(self.serialQueue, ^{
NSLog(@"這裡是同步執行的序列佇列02。===> %@", [NSThread currentThread]);
});
dispatch_async(self.serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"序列02 ===> %d ===> %@", i, [NSThread currentThread]);
}
});
dispatch_async(self.serialQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"序列03 ===> %d ===> %@", i, [NSThread currentThread]);
}
});
}
然後我們看一下輸出結果。我們會發現使用同步方式執行的任務都會被立即執行,並且是在當前執行緒(主執行緒)中執行的。而用非同步方式的任務則會在新建立的執行緒(number=2)中執行,並且它們是按照一定順序執行的(序列01->序列02->序列03)。
2016-02-26 22:17:34.745 GCDDemon[4084:422492] 這裡是同步執行的序列佇列01。===> <NSThread: 0x100103080>{number = 1, name = main}
2016-02-26 22:17:34.747 GCDDemon[4084:422514] 序列01 ===> 0 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
2016-02-26 22:17:34.747 GCDDemon[4084:422514] 序列01 ===> 1 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
2016-02-26 22:17:34.747 GCDDemon[4084:422514] 序列01 ===> 2 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
2016-02-26 22:17:34.748 GCDDemon[4084:422492] 這裡是同步執行的序列佇列02。===> <NSThread: 0x100103080>{number = 1, name = main}
2016-02-26 22:17:34.748 GCDDemon[4084:422514] 序列02 ===> 0 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
2016-02-26 22:17:34.748 GCDDemon[4084:422514] 序列02 ===> 1 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
2016-02-26 22:17:34.748 GCDDemon[4084:422514] 序列02 ===> 2 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
2016-02-26 22:17:34.748 GCDDemon[4084:422514] 序列03 ===> 0 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
2016-02-26 22:17:34.748 GCDDemon[4084:422514] 序列03 ===> 1 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
2016-02-26 22:17:34.748 GCDDemon[4084:422514] 序列03 ===> 2 ===> <NSThread: 0x100103a70>{number = 2, name = (null)}
我們再來看看如果是併發佇列的話,使用同步方式執行任何則和序列佇列一樣。而使用非同步方式執行任務的話,新新增的任務都會放到新建立的執行緒,即每個任務在單獨執行緒中,並且所有的任務都是併發執行的,而不像序列佇列是有順序的。如果是非同步方式執行的話,則每個任務都會新開一個執行緒,並且這些任務是併發執行的。我們來看一下下面這個例子。
-(void)testConcurrentQueue
{
//建立併發佇列,傳入引數為DISPATCH_QUEUE_CONCURRENT
self.concurrentQueue = dispatch_queue_create("com.lysongzi.concurrent", DISPATCH_QUEUE_CONCURRENT);
//同步執行佇列中的任務,會立即執行該任務
dispatch_sync(self.concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"併發同步01 ===> %d ===> %@", i, [NSThread currentThread]);
}
});
dispatch_sync(self.concurrentQueue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"併發同步02 ===> %d ===> %@", i, [NSThread currentThread]);
}
});
//非同步執行併發佇列
dispatch_async(self.concurrentQueue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"併發非同步01 ===> %d ===> %@", i, [NSThread currentThread]);
}
});
dispatch_async(self.concurrentQueue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"併發非同步02 ===> %d ===> %@", i, [NSThread currentThread]);
}
});
dispatch_async(self.concurrentQueue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"併發非同步03 ===> %d ===> %@", i, [NSThread currentThread]);
}
});
}
然後我們來看一下輸出結果。這剛好驗證了我們的說法。
2016-02-26 22:43:17.553 GCDDemon[4223:436777] 併發同步01 ===> 0 ===> <NSThread: 0x100108c80>{number = 1, name = main}
2016-02-26 22:43:17.554 GCDDemon[4223:436777] 併發同步01 ===> 1 ===> <NSThread: 0x100108c80>{number = 1, name = main}
2016-02-26 22:43:17.554 GCDDemon[4223:436777] 併發同步01 ===> 2 ===> <NSThread: 0x100108c80>{number = 1, name = main}
2016-02-26 22:43:17.554 GCDDemon[4223:436777] 併發同步02 ===> 0 ===> <NSThread: 0x100108c80>{number = 1, name = main}
2016-02-26 22:43:17.554 GCDDemon[4223:436777] 併發同步02 ===> 1 ===> <NSThread: 0x100108c80>{number = 1, name = main}
2016-02-26 22:43:17.554 GCDDemon[4223:436777] 併發同步02 ===> 2 ===> <NSThread: 0x100108c80>{number = 1, name = main}
2016-02-26 22:43:17.555 GCDDemon[4223:436826] 併發非同步03 ===> 0 ===> <NSThread: 0x100700110>{number = 3, name = (null)}
2016-02-26 22:43:17.555 GCDDemon[4223:436826] 併發非同步03 ===> 1 ===> <NSThread: 0x100700110>{number = 3, name = (null)}
2016-02-26 22:43:17.555 GCDDemon[4223:436825] 併發非同步02 ===> 0 ===> <NSThread: 0x1002000a0>{number = 2, name = (null)}
2016-02-26 22:43:17.555 GCDDemon[4223:436826] 併發非同步03 ===> 2 ===> <NSThread: 0x100700110>{number = 3, name = (null)}
2016-02-26 22:43:17.555 GCDDemon[4223:436825] 併發非同步02 ===> 1 ===> <NSThread: 0x1002000a0>{number = 2, name = (null)}
2016-02-26 22:43:17.555 GCDDemon[4223:436826] 併發非同步03 ===> 3 ===> <NSThread: 0x100700110>{number = 3, name = (null)}
2016-02-26 22:43:17.555 GCDDemon[4223:436825] 併發非同步02 ===> 2 ===> <NSThread: 0x1002000a0>{number = 2, name = (null)}
2016-02-26 22:43:17.555 GCDDemon[4223:436826] 併發非同步03 ===> 4 ===> <NSThread: 0x100700110>{number = 3, name = (null)}
2016-02-26 22:43:17.556 GCDDemon[4223:436824] 併發非同步01 ===> 0 ===> <NSThread: 0x10010aec0>{number = 4, name = (null)}
2016-02-26 22:43:17.582 GCDDemon[4223:436825] 併發非同步02 ===> 3 ===> <NSThread: 0x1002000a0>{number = 2, name = (null)}
2016-02-26 22:43:17.582 GCDDemon[4223:436824] 併發非同步01 ===> 1 ===> <NSThread: 0x10010aec0>{number = 4, name = (null)}
2016-02-26 22:43:17.582 GCDDemon[4223:436825] 併發非同步02 ===> 4 ===> <NSThread: 0x1002000a0>{number = 2, name = (null)}
2016-02-26 22:43:17.582 GCDDemon[4223:436824] 併發非同步01 ===> 2 ===> <NSThread: 0x10010aec0>{number = 4, name = (null)}
2016-02-26 22:43:17.582 GCDDemon[4223:436824] 併發非同步01 ===> 3 ===> <NSThread: 0x10010aec0>{number = 4, name = (null)}
2016-02-26 22:43:17.582 GCDDemon[4223:436824] 併發非同步01 ===> 4 ===> <NSThread: 0x10010aec0>{number = 4, name = (null)}