詳解IOS開發(fā)應(yīng)用之并發(fā)Dispatch Queues
詳解IOS開發(fā)應(yīng)用之并發(fā)Dispatch Queues是本文喲啊介紹的內(nèi)容,我們幾乎可以調(diào)度隊列去完成所有用線程來完成的任務(wù)。調(diào)度隊列相對于線程代碼更簡單,易于使用,更高效。下面講主要簡述調(diào)度隊列,在應(yīng)用中如何使用調(diào)度隊列去執(zhí)行任務(wù)。
1、關(guān)于調(diào)度隊列
所有的調(diào)度隊列都是先進(jìn)先出隊列,因此,隊列中的任務(wù)的開始的順序和添加到隊列中的順序相同。GCD自動的為我們提供了一些調(diào)度隊列,我們也可以創(chuàng)建新的用于具體的目的。
下面列出幾種可用的調(diào)度隊列類型以及如何使用。
(1)serial queues(串行隊列)又稱私有調(diào)度隊列(private),一般用再對特定資源的同步訪問上。我們可以根據(jù)需要創(chuàng)建任意數(shù)量的串行隊列,每一個串行隊列之間是并發(fā)的。
(2)并行隊列,又稱global dispatch queue。并行隊列雖然可以并發(fā)的執(zhí)行多個任務(wù),但是任務(wù)開始執(zhí)行的順序和其加入隊列的順序相同。我們自己不能去創(chuàng)建并行調(diào)度隊列。只有三個可用的global concurrent queues。
(3)main dispatch queue 是一個全局可用的串行隊列,其在行用程序的主線程上執(zhí)行任務(wù)。此隊列的任務(wù)和應(yīng)用程序的主循環(huán)(run loop)要執(zhí)行的事件源交替執(zhí)行。因為其運行在應(yīng)用程序的主線程,main queue經(jīng)常用來作為應(yīng)用程序的一個同步點。
2、關(guān)于隊列的一些技術(shù)
除了調(diào)度隊列,GCD還提供了一些有用的技術(shù)來幫助我們管理代碼。
- dispath group ,dispatch semaphore, dispath sources
3、使用blocks去實現(xiàn)tasks
block objects是基于C語言的特征,可以用在C,C++ Objective-c中。一個block雖然和函數(shù)指針有些相似,但是實際上代表一個底層數(shù)據(jù)結(jié)構(gòu),類似與對象,有編譯器去創(chuàng)建和管理。
block的一個優(yōu)勢是可以使用其自己作用域外的變量,例如,一個block可以讀取其父作用域的變量值,此值是copy到了block heap的數(shù)據(jù)結(jié)構(gòu)中。當(dāng)block被加入到dispatch queue中,這些值通常為只讀形式。
block的聲明和函數(shù)指針類似,只是把*改為了^,我們可以傳遞參數(shù)給block,也可以接收其返回的值。
4、創(chuàng)建和管理調(diào)度隊列
(1)獲得全局并發(fā)調(diào)度隊列(global concurrent dispath queues)
系統(tǒng)給每一個應(yīng)用程序提供了三個concurrent dispatch queues。這三個并發(fā)調(diào)度隊列是全局的,它們只有優(yōu)先級的不同。因為是全局的,我們不需要去創(chuàng)建。我們只需要通過使用函數(shù)dispath_get_global_queue去得到隊列,如下:
- dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
除了得到default的并發(fā)隊列,還可以通過傳遞參數(shù)DISPATCH_QUEUE_PRIOPITY_HIGH和DISPATCH_QUEUE_PRIOPITY_LOW去得到高優(yōu)先級或者低優(yōu)先級的。(第二個參數(shù)是為以后擴(kuò)展保留的)
雖然dispatch queue是引用計數(shù)對象,但是在此因為隊列是全局的,不需要我們?nèi)etain或者release,我們需要使用的時候直接調(diào)用函數(shù)dispath_get_global_queue就可以。
(2)創(chuàng)建串行調(diào)度隊列
當(dāng)想要任務(wù)按照某一個特定的順序執(zhí)行時,串行隊列是很有用的。串行隊列在同一個時間只執(zhí)行一個任務(wù)。我們可以使用串行隊列代替鎖去保護(hù)共享的數(shù)據(jù)。和鎖不同,一個串行隊列可以保證任務(wù)在一個可預(yù)知的順序下執(zhí)行。
和并發(fā)隊列不同,我們要自己去創(chuàng)建和管理串行隊列,可以創(chuàng)建任意數(shù)量的串行隊列。當(dāng)我們創(chuàng)建串行隊列時,應(yīng)出于某種目的,如保護(hù)資源,或者同步應(yīng)用程序的某些關(guān)鍵行為。
下面的代碼表述了怎么創(chuàng)建一個自定義的串行隊列,函數(shù)dispath_queue_create需要兩個參數(shù),隊列的名字,隊列的屬性。調(diào)試器和性能工具顯示隊列的名字幫助我們?nèi)ジ櫲蝿?wù)是如何執(zhí)行,隊列的屬性被保留供將來使用,應(yīng)該為NULL
- dispatch_queue_t queue;
- queue = dispatch_queue_create("com.example.MyQueue", NULL);
除了自己創(chuàng)建的自定義隊列,系統(tǒng)會自動的給我創(chuàng)建一個串行隊列并和應(yīng)用程序的主線程綁定到一起。下面講述如何獲得它。
(3)運行時獲得常見的隊列
GCD提供了一些函數(shù)讓我們能夠方便的訪問到common dispatch queues
使用dispatch_get_current_queue函數(shù)用來調(diào)試或者測試獲得當(dāng)前隊列的標(biāo)識。
使用函數(shù)dispatch_get_main_queue可以得到與應(yīng)用程序主線程相連的串行調(diào)度隊列。
(4)調(diào)度隊列的內(nèi)存管理
調(diào)度隊列是引用計數(shù)類型,當(dāng)我們創(chuàng)建串行調(diào)度隊列時,我們要release它。可以使用函數(shù)dispatch_retain和dispatch_release去增加或者減少引用計數(shù)。
(5)在一個隊列中存儲自定義context information
所有的調(diào)度對象允許我們讓其與一個自定義上下文數(shù)據(jù)關(guān)聯(lián),通過函數(shù)dispatch_set_context和dispatch_get_context來使用,系統(tǒng)不會去使用我們的自定義數(shù)據(jù),我們自己在恰當(dāng)?shù)臅r間去分配和釋放。
對于隊列,上下文數(shù)據(jù)通常用來存儲一個指向?qū)ο蟮闹羔?,或者其他的?shù)據(jù)結(jié)構(gòu),我們可以在隊列的finalizer函數(shù)中去釋放context data。下面將給一個例子。
(6)為隊列提供一個clean up 函數(shù)。
當(dāng)我們創(chuàng)建串行調(diào)度隊列之后,我們可以讓其和一個finalizer函數(shù)相連用來清理隊列中需要清理的數(shù)據(jù)。我們可以使用dispatch_set_finalizer_f函數(shù)去設(shè)置一個函數(shù),當(dāng)隊列的引用計數(shù)為0時會去自動的調(diào)用。使用此函數(shù)去清理和隊列相關(guān)聯(lián)的context data,當(dāng)context 指針不會NULL時,此函數(shù)就會調(diào)用。
- 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、在隊列中添加一個任務(wù)
(1)有兩種方式在隊列中添加一個任務(wù),同步或者異步。盡可能使用dispatch_async和dispatch_async_f 函數(shù)去執(zhí)行,比同步的要***。當(dāng)我們向隊列中添加一個塊對象或者函數(shù)時,我們沒有方法去知道此代碼什么時間執(zhí)行。
使用此異步不會去阻塞主線程。
雖然盡可能異步添加任務(wù),在有些時候同步的方式去添加一個任務(wù)會防止一些同步錯誤。同步的方式調(diào)用函數(shù)dispatch_sync和dispatch_sync_f。此函數(shù)阻塞主線程的執(zhí)行,直到指定的任務(wù)完成。
下面是代碼例子:
(2)在任務(wù)完成的時候執(zhí)行completion block
當(dāng)任務(wù)完成時,我們應(yīng)用程序需要得到通知,一遍去合并結(jié)果,在傳統(tǒng)的異步編程中,我們可能會使用回調(diào)函數(shù),但是在調(diào)度隊列中,我們使用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)并發(fā)的執(zhí)行循環(huán)迭代(loop iterations)
對于for循環(huán),如果每一次的迭代相互都沒有影響,可以并發(fā)的去執(zhí)行迭代,使用函數(shù)dispatch_apply或者dispatch_apply_f 函數(shù).
和正常的循環(huán)一樣,函數(shù)dispatch_apply或者dispatch_apply_f直到所有的循環(huán)迭代完成時才返回。
如下代碼:
- dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- dispatch_apply(count, queue, ^(size_t i) {
- printf("%un",i);
- });
(4)在主線程上執(zhí)行任務(wù)
我們可以通過調(diào)用函數(shù)dispatch_get_main_queue 去去得到主線程的調(diào)度隊列。
小結(jié):詳解IOS開發(fā)應(yīng)用之并發(fā)Dispatch Queues的內(nèi)容介紹完了,希望通過本文的學(xué)習(xí)能對你有所幫助!