詳解IOS開發應用之并發Dispatch Queues
詳解IOS開發應用之并發Dispatch Queues是本文喲啊介紹的內容,我們幾乎可以調度隊列去完成所有用線程來完成的任務。調度隊列相對于線程代碼更簡單,易于使用,更高效。下面講主要簡述調度隊列,在應用中如何使用調度隊列去執行任務。
1、關于調度隊列
所有的調度隊列都是先進先出隊列,因此,隊列中的任務的開始的順序和添加到隊列中的順序相同。GCD自動的為我們提供了一些調度隊列,我們也可以創建新的用于具體的目的。
下面列出幾種可用的調度隊列類型以及如何使用。
(1)serial queues(串行隊列)又稱私有調度隊列(private),一般用再對特定資源的同步訪問上。我們可以根據需要創建任意數量的串行隊列,每一個串行隊列之間是并發的。
(2)并行隊列,又稱global dispatch queue。并行隊列雖然可以并發的執行多個任務,但是任務開始執行的順序和其加入隊列的順序相同。我們自己不能去創建并行調度隊列。只有三個可用的global concurrent queues。
(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它??梢允褂煤瘮礵ispatch_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。此函數阻塞主線程的執行,直到指定的任務完成。
下面是代碼例子:
(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("%un",i);
- });
(4)在主線程上執行任務
我們可以通過調用函數dispatch_get_main_queue 去去得到主線程的調度隊列。
小結:詳解IOS開發應用之并發Dispatch Queues的內容介紹完了,希望通過本文的學習能對你有所幫助!