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