1. 程式人生 > >iOS-多執行緒程式設計學習之GCD——序列佇列和併發佇列(五)

iOS-多執行緒程式設計學習之GCD——序列佇列和併發佇列(五)

  Grand Central Dispatch(GCD)有很多部分構成,例如有很好的語言特性,執行庫,還提供了系統的、高效的方式來支援具有多核處理器的iOS和OS X裝置進行併發事件處理。
  BSD子系統,CoreFoundation和Cocoa APIs都已經使用這個增強特性來進行擴充套件了,因為它可以使得你的系統和應用執行的更快,更有效率,響應更及時。GCD是執行在系統級別的,他可以更好的滿足應用執行時的需求,更合理排程有限的系統資源。
  GCD並不是被限制只能在系統級別應用上使用的,我們可以在更高層的應用上也使用它。但是一般情況下我們還可以考慮Cocoa中是否有類似的物件可以實現相似的功能,例如NSOperation,它也提供了很簡單便捷的方式來進行併發任務處理【

傳送門——iOS-多執行緒程式設計學習之NSOperation(四)】。

一、GCD與ARC

  當使用Objective-C來構建應用時,所有的dispatch obeject都是Objective-C型別的物件。所以當在應用中開啟了ARC(自動引用計數)時,這些dispatch obeject也會想OC的物件一樣被自動的傳送retainrelease訊息。當ARC被禁用時(也就是進行MRC手動記憶體管理時),可以通過使用dispatch_retaindispatch_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)}

原始碼

參考資料