dispatch queues GCD

咱們幾乎能夠調度隊列去完成全部用線程來完成的任務。調度隊列相對於線程代碼更簡單,易於使用,更高效。編程

    下面講主要簡述調度隊列,在應用中如何使用調度隊列去執行任務。數據結構


1.關於調度隊列併發

   全部的調度隊列都是先進先出隊列,所以,隊列中的任務的開始的順序和添加到隊列中的順序相同。GCD自動的爲咱們提供了一些調度隊列,咱們也能夠建立新的用於具體的目的。app

   下面列出幾種可用的調度隊列類型以及如何使用。異步

(1)serial queues(串行隊列)又稱私有調度隊列(private),通常用在對特定資源的同步訪問上。咱們能夠根據須要建立任意數量的串行隊列,每個串行隊列之間是併發的。async

(2)並行隊列,又稱global dispatch queue。並行隊列雖然能夠併發的執行多個任務,可是任務開始執行的順序和其加入隊列的順序相同。咱們本身不能去建立並行調度隊列。只有三個可用的global concurrent queues。ide

Dispatch Queues異步編程

Dispatch Queues從使用的角度將更象另外一種形式的Operation Queues只是 Operation Queues是用Object C的Dispatch Queues是C的函數

dispatch Queues有serial Queues 也被稱爲私有dispatch Queues,一個時間只能運行一個task,順序運行工具

dispatch_queue_t queue;

queue = dispatch_queue_create("myQueue", NULL);  

dispatch_async(queue, ^{

        printf("Do some work here.\n");

    });

    printf("The first block may or may not have run.\n");

    dispatch_sync(queue, ^{

        printf("Do some more work here.\n");

    });

    printf("Both blocks have completed.\n");

這裏使用了同步dispatch和異步dispatch,推薦使用dispatch_async這樣才能真正體現其中的優點同步至關於WaitUntilDone = YES

 

還有一種就是Concurrent Queues每一個程序系統自動提供了3個Concurrent Queues

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_queue_t aHQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

 dispatch_queue_t aLQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

啥意思一看就明白,3個優先級別的concurrent queues

 

最後一個特殊的Dispatch Queue就是main dispatch Queue 也是程序啓動自動生成

dispatch_queue_t mainQueue = dispatch_get_main_queue();

 

concurrent queues和main queue 都是由系統生成並且 dispatch_suspend, dispatch_resume, dispatch_set_context,這些函數對他們無效


(3)main dispatch queue 是一個全局可用的串行隊列,其在行用程序的主線程上執行任務。此隊列的任務和應用程序的主循環(run loop)要執行的事件源交替執行。由於其運行在應用程序的主線程,main queue常常用來做爲應用程序的一個同步點。


2.關於隊列的一些技術

除了調度隊列,GCD還提供了一些有用的技術來幫助咱們管理代碼。

dispath group ,dispatch semaphore, dispath sources


3.使用blocks去實現tasks

block objects是基於C語言的特徵,能夠用在C,C++ Objective-c中。一個block雖然和函數指針有些類似,可是實際上表明一個底層數據結構,相似與對象,有編譯器去建立和管理。

block的一個優點是可使用其本身做用域外的變量,例如,一個block能夠讀取其父做用域的變量值,此值是copy到了block heap的數據結構中。當block被加入到dispatch queue中,這些值一般爲只讀形式。

block的聲明和函數指針相似,只是把*改成了^,咱們能夠傳遞參數給block,也能夠接收其返回的值。


4.建立和管理調度隊列

(1)得到全局併發調度隊列(global concurrent dispath queues)

系統給每個應用程序提供了三個concurrent dispatch queues。這三個併發調度隊列是全局的,它們只有優先級的不一樣。由於是全局的,咱們不須要去建立。咱們只須要經過使用函數dispath_get_global_queue去獲得隊列,以下:

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

除了獲得default的併發隊列,還能夠經過傳遞參數DISPATCH_QUEUE_PRIOPITY_HIGH和DISPATCH_QUEUE_PRIOPITY_LOW去獲得高優先級或者低優先級的。(第二個參數是爲之後擴展保留的)

雖然dispatch queue是引用計數對象,可是在此由於隊列是全局的,不須要咱們去retain或者release,咱們須要使用的時候直接調用函數dispath_get_global_queue就能夠。


(2)建立串行調度隊列

當想要任務按照某一個特定的順序執行時,串行隊列是頗有用的。串行隊列在同一個時間只執行一個任務。咱們可使用串行隊列代替鎖去保護共享的數據。和鎖不一樣,一個串行隊列能夠保證任務在一個可預知的順序下執行。

和併發隊列不一樣,咱們要本身去建立和管理串行隊列,能夠建立任意數量的串行隊列。當咱們建立串行隊列時,應出於某種目的,如保護資源,或者同步應用程序的某些關鍵行爲。

下面的代碼表述了怎麼建立一個自定義的串行隊列,函數dispath_queue_create須要兩個參數,隊列的名字,隊列的屬性。調試器和性能工具顯示隊列的名字幫助咱們去跟蹤任務是如何執行,隊列的屬性被保留供未來使用,應該爲NULL

dispatch_queue_t queue; 

queue = dispatch_queue_create("com.example.MyQueue", NULL); 

除了本身建立的自定義隊列,系統會自動的給我建立一個串行隊列並和應用程序的主線程綁定到一塊兒。下面講述如何得到它。


(3)運行時得到常見的隊列

GCD提供了一些函數讓咱們可以方便的訪問到common dispatch queues

   使用dispatch_get_current_queue函數用來調試或者測試得到當前隊列的標識。

   使用函數dispatch_get_main_queue能夠獲得與應用程序主線程相連的串行調度隊列。


(4)調度隊列的內存管理

調度隊列是引用計數類型,當咱們建立串行調度隊列時,咱們要release它。可使用函數dispatch_retain和dispatch_release去增長或者減小引用計數。


(5)在一個隊列中存儲自定義context information

全部的調度對象容許咱們讓其與一個自定義上下文數據關聯,經過函數dispatch_set_context和dispatch_get_context來使用,系統不會去使用咱們的自定義數據,咱們本身在恰當的時間去分配和釋放。


對於隊列,上下文數據一般用來存儲一個指向對象的指針,或者其餘的數據結構,咱們能夠在隊列的finalizer函數中去釋放context data。下面將給一個例子。


(6)爲隊列提供一個clean up 函數。

當咱們建立串行調度隊列以後,咱們可讓其和一個finalizer函數相連用來清理隊列中須要清理的數據。咱們可使用 dispatch_set_finalizer_f函數去設置一個函數,當隊列的引用計數爲0時會去自動的調用。使用此函數去清理和隊列相關聯的 context data,當context 指針不會NULL時,此函數就會調用。

shows a custom finalizer function and a function that creates a queue and installs that finalizer. The queue uses the finalizer function to release the data stored in the queue’s context pointer. (The myInitializeDataContextFunction and myCleanUpDataContextFunction functions referenced from the code are custom functions that you would provide to initialize and clean up the contents of the data structure itself.) The context pointer passed to the finalizer function contains the data object associated with the queue.

void myFinalizerFunction(void *context) 

    MyDataContext* theData = (MyDataContext*)context; 

  

    // Clean up the contents of the structure 

    myCleanUpDataContextFunction(theData); 

  

    // Now release the structure itself. 

    free(theData); 

  

dispatch_queue_t createMyQueue() 

    MyDataContext*  data = (MyDataContext*) malloc(sizeof(MyDataContext)); 

    myInitializeDataContextFunction(data); 

  

    // Create the queue and set the context data. 

    dispatch_queue_t serialQueue = dispatch_queue_create("com.example.CriticalTaskQueue", NULL); 

    if (serialQueue) 

    { 

        dispatch_set_context(serialQueue, data); 

        dispatch_set_finalizer_f(serialQueue, &myFinalizerFunction); 

    } 

  

    return serialQueue; 


5.在隊列中添加一個任務

(1)

有兩種方式在隊列中添加一個任務,同步或者異步。儘量使用dispatch_async和dispatch_async_f 函數去執行,比同步的要首選。當咱們向隊列中添加一個塊對象或者函數時,咱們沒有方法去知道此代碼什麼時間執行。

使用此異步不會去阻塞主線程。

雖然儘量異步添加任務,在有些時候同步的方式去添加一個任務會防止一些同步錯誤。同步的方式調用函數dispatch_sync和dispatch_sync_f。此函數阻塞主線程的執行,直到指定的任務完成。

下面是代碼例子:

dispatch_queue_t myCustomQueue; 

myCustomQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL); 

  

dispatch_async(myCustomQueue, ^{ 

    printf("Do some work here.\n"); 

}); 

  

printf("The first block may or may not have run.\n"); 

  

dispatch_sync(myCustomQueue, ^{ 

    printf("Do some more work here.\n"); 

}); 

printf("Both blocks have completed.\n"); 


(2)在任務完成的時候執行completion block

   當任務完成時,咱們應用程序須要獲得通知,一遍去合併結果,在傳統的異步編程中,咱們可能會使用回調函數,可是在調度隊列中,咱們使用completion block。

void average_async(int *data, size_t len, 

   dispatch_queue_t queue, void (^block)(int)) 

   // Retain the queue provided by the user to make 

   // sure it does not disappear before the completion 

   // block can be called. 

   dispatch_retain(queue); 

  

   // Do the work on the default concurrent queue and then 

   // call the user-provided block with the results. 

   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 

      int avg = average(data, len); 

      dispatch_async(queue, ^{ block(avg);}); 

  

      // Release the user-provided queue when done 

      dispatch_release(queue); 

   }); 

(3)併發的執行循環迭代(loop iterations)

對於for循環,若是每一次的迭代相互都沒有影響,能夠併發的去執行迭代,使用函數dispatch_apply或者dispatch_apply_f 函數.

和正常的循環同樣,函數dispatch_apply或者dispatch_apply_f直到全部的循環迭代完成時才返回。

以下代碼:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

  

dispatch_apply(count, queue, ^(size_t i) { 

   printf("%u\n",i); 

}); 

(4)在主線程上執行任務

咱們能夠經過調用函數dispatch_get_main_queue 去去獲得主線程的調度隊列。

相關文章
相關標籤/搜索