源碼:https://github.com/baitxaps/Block
一、GCD實現
1>GCD實現需要使用的一些工具:
.用于管理追加的Block的C語言層實現的FIFO隊列
.Atomic 函數中實現的用于排他控制的輕量級信號
.用于管理線程的C語言層實現的一些容器
但是還要內核級的實現,通常,應用程序中編寫的線程管理用的代碼要在系統級(iOS和OS X的核心級)實現
因此,無論編程人員如何努力編寫管理線程的代碼,在性能方面也不能勝過XNU內核級所實現的GCD。
使用使用GCD要比使用pthreads和NSThread這些一般的多線程編程API更好,并且,如果使用GCD就不必
編寫這操作線程反復出現類似的源代碼(因定源代碼片斷),而可以在線程中集中實現處理內容。盡量多使用
GCD或者用Cocoa框架GCD的NSOperationQueue類的API
2>用于實現Dispatch Queue而使用的軟件組件
組件名稱 提供技術
libdispatch Dispatch Queue
libc(pthreads) pthread_workqueue
XUN內核 workqueue
3>編程人員所使用GCD的API全部為包含有libdispatch庫的C函數,Dispatch Queue通過結構體和鏈表,被
實現為FIFO隊列,FIFO隊列管理是通過dispatch_async()等函數所追加的Block,Block并不是直接加入FIFO
隊列,而是先加入Dispatch Continuation這一dispatch_continuation_t類型結構體中,然后再加入FIFO
隊列。Dispatch Continuation用于記憶Block所屬的Dispatch Group和其他一些信息(執行上下文)
4>Dispatch Queue可通過dispatch_set_target_queue()設定,可以設定執行該Dispatch Queue處理的
Dispatch Queue為目標。該目標可像串珠子一樣,設定多個連接在一起的Dispatch Queue,但是在連接串的最后
必須設定Main Dispatch Queue,或各種優先級的Global Dispatch Queue,或是準備用于Serial Dispatch Queue
的Global Dispatch Queue
5>Global Dispatch Queue的8種優先級:
.High PRiority
.Default Priority
.Low Priority
.Background Priority
.High Overcommit Priority
.Default Overcommit Priority
.Low Overcommit Priority
.Background Overcommit Priority
附有Overcommit的Global Dispatch Queue使用在Serial Dispatch Queue中,不管系統狀態如何,都會強
制生成線程的 Dispatch Queue。
這8種Global Dispatch Queue各使用1個pthread_workqueue,GCD初始化時,使用pthread_workqueue_create_np()
生成pthread_workqueue
pthread_workqueue使用bsdthread_register和workq_open系統調用,在初始化XUN內核的workqueue之后獲取workqueue
信息
6>XUN內核的4種workqueue的優先級,與Global Dispatch Queue的4種執行優先級相同
.WORKQUEUE_HIGH_PRIQUEUE
.WORKQUEUE_DEFAULT_PRIQUEUE
.WORKQUEUE_LOW_PRIQUEUE
.WORKQUEUE_BG_PRIQUEUE
7>Dispatch Queue中執行Block過程
當在Global Dispatch Queue中執行Block時,libdispatch從Global Dispatch Queue自身的FIFO隊列中取
出Dispatch Continuation,調用pthread_workqueue_additem_np(),將該Global Dispatch Queue自身、符
合優先級的workqueue信息以及為執行Dispatch Continuation的回調函數等傳遞給參數。
thread_workqueue_additem_np()使用workq_kernreturn系統調用,通知workqueue嗇應當執行的項目,根據該
通知,XUN內核基于系統狀態判斷是否要生成線程,如果是Overcommit優先級的Global Dispatch Queue,workqueue
則始終生成線程。
.該線程雖然與iOS和OS X中通常使用的線程大致相同,但是有一部分pthread API不能使用
.workqueue生成的線程在實現用于workqueue的線程計劃表中運行,與一般線程的上下文切不同,這里也隱藏著使用GCD的
原因
.workqueue的線程執行pthread_workqueue(),該函數用libdispatch的回調函數,在回調函數中執行執行加入到
Dispatch Continuatin的Block
.Block執行結束后,進行通知Dispatch Group結束,釋放Dispatch Continuation等處理,開始準備執行加入到Dispatch Continuation中的下一個Block
二、常用術語介紹
1.多線程
由于一個CPU一次只能執行一個命令,不能執行某處分開的并列的兩個命令,因此通過CPU執行的CPU命令列就
好比一條無分叉的大道,其執行不會出現分歧
一個CPU執行的CPU命令列為一條無分叉路徑:線程,這時無分叉路徑不只1條,存在有多條時即:多線程。
現在一個物理CPU芯片實際上有64個(64核)CPU,在多線程中,1個CPU核執行多條不同路徑上的不同命令。
2.上下文切換
1個CPU核一次能夠執行的CPU命令始終為1,OS X和iOS的核心XUN內核在發生操作系統事件時(如每隔一定時間,
喚起系統調用等情況)會切換執行路徑。執行中路徑的狀態,如CPU的寄存器等信息保存到各自路徑專用的內存塊中,
從切換目標路徑專用的內存塊中,復原CPU寄存器等信息,繼續執行切換路徑的CPU命令列:上下文切換
由于使用多線程的程序可以在某個線程和其他線程之間反復多次進行上下文切換,因些看上去就像1個CPU核能夠
并列執行多個線程一樣,而且在具有多個CPU核的情況下,就是真的提供了多個CPU核并行執行多個線程的技術
3.多線程問題
多線程實際上是一種易發生各種問題的編程技術,比如多個線程更新相同的資源會導致數據的不一致(數據競爭)、
停止等待事件的線程會導致多個線程相互技續等待(死鎖)、使用太多線程會消耗大量內存等
4.主線程
應用程序在啟動時,通過最先執行的線程。主線程來描繪用戶界面、處理觸摸屏幕的事件等。如果在該主線程中進行
長時間的處理,就會妨礙主線程的執行(阻塞),在OS和iOS的應用程序中,會妨礙主線程中被稱為RunLoop的主循環的
執行,從面導致不能更新用戶界面、應用程序的畫面長時間停滯等問題
三、常用GCD的API例子
#pragma mark - GCDTest
- (void)GCDTest{
//0.
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("blog.csdn.NET/baitxaps", NULL);
//1.
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("blog.csdn.com/baitxaps", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentDispatchQueue,^{
NSLog(@"Block on concurrentDispatchQueue");
});
#if !OS_OBJECT_USE_OBJC
//dispatch_release(serialDispatchQueue);
dispatch_release(concurrentDispatchQueue);
#endif
//2.各種Dispatch Queue獲取方法
/*
對于 Main Dispatch Queue和Global Dispatch Queue執行dispatch_retain、dispatch_release函數不會引用作何變化,
也不會有任何問題
*/
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue();//main Dispatch Queue
dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);//最高優先級
dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//默認優先級
dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);//默認優先級
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);//后臺優先級
//3.默認優先級的Global Dispatch Queue中執行Block
dispatch_async(globalDispatchQueueDefault, ^{
// 并行執行的處理
//...
dispatch_async(mainDispatchQueue, ^{
//只能在主線程中執行的處理
//...
});
});
//4.指定變更執行優先級(在后臺執行動作處理的Serial Dispatch Queue的生成方法)
dispatch_set_target_queue(serialDispatchQueue, globalDispatchQueueBackground);
//5.在指定時間后執行處理的情況,用dispatch_after
dispatch_time_t time =dispatch_time(DISPATCH_TIME_NOW, 3ull *NSEC_PER_MSEC);
dispatch_after(time, mainDispatchQueue, ^{
NSLog(@"waited at least 3 seconds.");
});
//6.dispatch_walltime()
dispatch_time_t seconds = getDispatchTimeByDate([NSDate date]);
NSLog(@"second = %llu",seconds);
//7.Dispatch Group
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, globalDispatchQueueDefault, ^{NSLog(@"blk0");});
dispatch_group_async(group, globalDispatchQueueDefault, ^{NSLog(@"blk1");});
dispatch_group_async(group, globalDispatchQueueDefault, ^{NSLog(@"blk2");});
dispatch_group_notify(group, mainDispatchQueue, ^{NSLog(@"done");});
#if !OS_OBJECT_USE_OBJC
dispatch_release(group);
#endif
//7.dispatch_group_wait()
dispatch_group_t group_wait = dispatch_group_create();
dispatch_group_async(group_wait, globalDispatchQueueDefault, ^{NSLog(@"blk0");});
dispatch_group_async(group_wait, globalDispatchQueueDefault, ^{NSLog(@"blk1");});
dispatch_group_async(group_wait, globalDispatchQueueDefault, ^{NSLog(@"blk2");});
dispatch_group_wait(group_wait, DISPATCH_TIME_FOREVER);
#if !OS_OBJECT_USE_OBJC
dispatch_release(group_wait);
#endif
//8.
time = dispatch_time(DISPATCH_TIME_NOW,1ull *NSEC_PER_SEC);
dispatch_group_t group_result = dispatch_group_create();
long result = dispatch_group_wait(group_result, time);
// long result = dispatch_group_wait(group_result, DISPATCH_TIME_NOW);
if (result == 0) {
//屬于Dispatch Group的全部內容處理執行結束
}else{
//屬于Dispatch Group的某一個處理還在執行中
}
//9.dispatch_barrier_async()
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk0 for reading");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk1 for reading");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk2 for reading");});
dispatch_barrier_async(concurrentDispatchQueue, ^{NSLog(@"blk3 for writing");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk4 for reading");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk5 for reading");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk6 for reading");});
//10.dispatch_apply()
//0>
dispatch_apply(10, globalDispatchQueueDefault, ^(size_t index) {
NSLog(@"dispatch_apply = %zu",index);
});
NSLog(@"dispatch_apply() done.");
//1>
NSArray *array = self.datas;
dispatch_apply(self.datas.count, globalDispatchQueueDefault, ^(size_t index){
NSLog(@"index= %zu,element = %@",index,array[index]);
});
//3>在Global Dispatch Queue中非同步執行
dispatch_async(globalDispatchQueueDefault, ^{
//Global Dispatch Queue 等待dispatch_apply函數中全部處理執行結束
dispatch_apply(self.datas.count, globalDispatchQueueDefault, ^(size_t index){
NSLog(@"index= %zu,element = %@",index,array[index]);
});
//dispatch_apply()中處理全部執行結束
//在Main Dispatch Queue中執行處理用戶界用更新等
dispatch_async(mainDispatchQueue, ^{NSLog(@"在Main Dispatch Queue中執行處理用戶界用更新等...,Done");});
});
//11.dispatch_suspend()/dispatch_resume()
/*
這些函數對已經執行的處理沒有影響,掛起后,追加到Dispatch Queue中但尚未執行的處理在些之扣停止執行,
而恢復則使用這些處理能夠繼續執行。
*/
dispatch_suspend(globalDispatchQueueDefault);
dispatch_resume(globalDispatchQueueDefault);
//12.dispatch Semaphore
//Dispatch Semaphore的計數初始值設定為1,保證可訪問的NSArray類的對象的線程同時只能有1個
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//1>
{
NSMutableArray *array = [NSMutableArray new];
for (int i = 0; i< 100000; i++) {
dispatch_async(globalDispatchQueueDefault, ^{
//等待Dispatch Semaphore,一直等待,直到Dispatch Semaphore的計數值達到大于等于1。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
/*
Dispatch Semaphore的計數值達到大于等于1,所以將Dispatch Semaphore的計數值減去1
dispatch_semaphore_wait()執行返回。即執行到此進的Dispatch Semaphore的計數值恒
為0,由于可訪問NSArray類對象的線程只有1個,因此可安全地進行更新。
*/
[array addObject:@(i)];
/*
排他控制處理結束,所以通過dispatch_semaphore_signal()將Dispatch Semaphore的計
數值加1。如果有通過dispatch_semaphore_wait通過()等待Dispatch Semaphore和計數值
增加的線程,就由最先等待的線程執行。
*/
dispatch_semaphore_signal(semaphore);
});
}
}
#if !OS_OBJECT_USE_OBJC
dispatch_release(semaphore);
#endif
//13.dispatch_once()
//更新標志變量
static int initialized = NO;
if(initialized == NO){
initialized = YES;
}
//可通過dispatch_once()簡化
static dispatch_once_t pred;
dispatch_once(&pred,^{
initialized = YES;
});
/*
通過dispatch_once(),該源代碼即使在多線程環境下執行,可保證100%安全
之前的源代碼在大多數情況下也是安全的,但在多核CPU中,在正在更新表示是否初始化的標志變量時讀取,就有
可能多次執行初始化處理。而用dispatch_once()初始化就不必擔心這樣的問題。
這就是所說的單例模式,在生成單例對象時使用。
*/
//14.死鎖下面幾種情況:
/*
//1>
dispatch_sync(mainDispatchQueue, ^{NSLog(@"死鎖1");});
//2>
dispatch_async(mainDispatchQueue, ^{
dispatch_sync(mainDispatchQueue, ^{NSLog(@"死鎖2");});
});
//3>
dispatch_async(serialDispatchQueue, ^{
dispatch_sync(serialDispatchQueue, ^{NSLog(@"死鎖3");});
});
*/
}
- (NSArray *)datas{
return @[@"obj1",@"obj2",@"obj3",@"obj4",@"obj5"];
}
dispatch_time_t getDispatchTimeByDate(NSDate *date){
NSTimeInterval interval;
double second,subsecond;
struct timespec time;
//以1970/01/01 GMT為基準時間,返回實例保存的時間與1970/01/01 GMT的時間間隔
interval = [date timeIntervalSince1970];
//分解interval,以得到interval的整數和小數部分
//返回值interval的小數部分 interval的整數部分
subsecond = modf(interval, &second);
time.tv_sec = second;
time.tv_nsec = subsecond *NSEC_PER_SEC;
dispatch_time_t milestone = dispatch_walltime(&time, 0);
return milestone;
}
四、對以上例子功能點進行逐一說明
5.GCD
使用多線程編程,在執行長時間的處理時仍可保證用戶界面的響應性能,GCD大大簡化了編于復雜的多本程編程源代碼
開發者要做的只是定義想執行的任務并追加到適當的Dispatch Queue中
1> dispatch_async(queue,^{
//想執行的任務
});
定義一個Block語法,dispatch_async函數追加賦值在變量queue的Dispatch Queue中:指定的Block在另一線程中執行
2>Dispatch Queue就是執行處理的等待隊列,應用程序編程人員過dispatch_async()等API,在Block語法中記述想要執行
的處理并將其追加到Dispatch Queue中,Dispatch Queue按照追加的順序FIFO執行處理。另外在執行處理時存在兩種
Dispatch Queue,一種是等待現在執行處理的Serial Dispatch Queue結束,另一種是不等等待現在執行的Concurrent Dispatch Queue結束.
比較這兩種Dispatch Queue,在dispatch_async中追加多個處理,源碼如下:
dispathc_async(queue,blk0);
dispathc_async(queue,blk1);
dispathc_async(queue,blk2);
dispathc_async(queue,blk3);
dispathc_async(queue,blk4);
dispathc_async(queue,blk5);
dispathc_async(queue,blk6);
當變量queue為Serial Dispatch Queue時,因為要等待現在執行中的處理結束,所以首先執行blk0,blko執行結束后,接
著執行blk1,blk1執行結束后再執行blk2,...,同時執行的處理只能有1個,即執行該源代碼后,一定按照以下順序進行處理:
blk0
blk1
blk2
blk3
blk4
blk5
blk6
當變量queue為Concurrent Dispatch Queue時,因為不用等待現在執行中的處理結束,所以首先執行blk0,不管blko執行結束后,都
開始執行blk1,不管blk1執行結束,都開始再執行blk2,...,這樣雖然不用等待處理結束,可以并行多個處理,但并行的處理數取決于當前
系統的狀態:iOS和OS X基于Dispatch Queue中的處理數、CPU核數以及CPU負荷當前系統的狀態來決定Concurrent Dispatch Queue
中并行執行的得理數。
使用多個線程同進執行多個處理:并行執行
3>iOS和OS X的核心--XNU內核決定應當使用的線程數,并只生成所需的線程執行處理。另外,當處理結束,應當執行的處理數減少時,XUN
內核會結束不再需要的線程。XUN內核僅用Concurrent Dispatch Queue便可完美地管理并行執行多人處理的線程
4>如何得到Dispatch Queue
.通過GCD的API dispatch_queue_create()生成Dispatch Queue
.獲取系統標準提供的Dispatch Queue
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("blog.csdn.Net/baitxaps",NULL);
Serial Dispatch Queue注意:
當生成多個Serial Dispatch Queue時,各個Serial Dispatch Queue將并行執行,雖然在1個Serial Dispatch Queue中
同進只能執行一個追加處理,但如果將處理分別追加到4個Serial Dispatch Queue中,各個Serial Dispatch Queue執行1個,
即為同時執行4個處理
一旦生成Serial Dispatch Queue并追加處理,系統對于一個Serial Dispatch Queue就只生成并使用一個線程。如果生成
1000個Serial Dispatch Queue,那么就生成1000個線程,過多使用多線程,就會消耗大量內存,引起大量的上下文切換,大
幅度降低系統的響應性能
只在為了避免多線程編程問題之一:多個線程更新相同資源導致數據競爭時使用Serial Dispatch Queue
Serial Dispatch Queue的生成個數應當僅限所必需的數量,eg:更新數據庫時1個表成1個Serial Dispatch Queue,
更新文件時1個文件或是可以分割的1個文件塊生成1個Serial Dispatch Queue。
當想并行執行不發生數據競爭等問題處理時,使用Concurrent Dispatch Queue,而且對于Concurrent Dispatch Queue來
說,不管生成多少,由于XUN內核只使用有效管理的線程,因此不會發生Serial Dispatch Queue那些問題
5>dispatch_queue_create()函數
.第一個參數指定Serial Dispatch Queue的名稱(可為NULL),該名稱在Xcode和Instruments調試器中作為Dispathc Queue名稱表示
該名稱出會出現在應用程序崩潰時所生成的CrashLog中
第二個參數指定為NULL
.生成Concurrent Dispatch Queue時:
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("blog.csdn.com/baitxaps",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentDispatchQueue,^{
NSLog(@"Block on concurrentDispatchQueue");
});
該源代碼在Concurrent Dispatch Queue中執行指定的Block,盡管有ARC這一通過編譯器自動管理內存的優秀技術,但生成的Dispatch Queue
必須由程序員負責用dispatch_release函數釋放。這是因為Dispatch Queue并沒有像Block那樣具有作為OC對象來處理的技術
dispatch_release(concurrentDispatchQueue);
該名稱中含有release,由此可以推測出相應地也存在dispatch_retain();
#pragma mark - Main Dispatch Queue/Global Dispatch Queue
//6.Main Dispatch Queue/Global Dispatch Queue
獲取系統標準提供的Dispatch Queue:
Main Dispatch Queue:在主線程中執行Dispatch Queue
因為主線程只有1個,所以 Main Dispatch Queue自然就是Serial Dispatch Queue,追加到Main Dispatch Queue的處理在
主線程的RunLoop中執行(用戶界面更新等)
Global Dispatch Queue:所有應用程序都能夠使用的Concurrent Dispatch Queue
沒有必要通過dispatch_queue_create()逐個生成Concurrent Dispatch Queue,只要獲取Global Dispatch Queue使用即可
Global Dispatch Queue 4個執行優先級:
High Priority
Default Priority
Low Priority
Background Priority
通過XNU內核管理的用于Global Dispatch Queue的線程,將各自使用的Global Dispatch Queue 的
執行優先級作為線程的執行優先級使用,在向Global Dispatch Queue追加處理時,應選擇與處理內容對
應的執行優先級的Global Dispatch Queue。通過XNU內核用于Global Dispatch Queue的線程并不能保證
實時性,因此執行優先級只是大致的判斷
#pragma mark -dispatch_set_target_queue
//7.dispatch_set_target_queue
1>dispatch_set_target_queue()生成的Dispatch Queue不管是Serial Dispatch Queue還是Concurrent
Dispatch Queue,都使用與默認優先級Global Dispatch Queue相同執行的線程。而變更生成的Dispatch Queue
的執行優先級要使用dispatch_set_target_queue(),在后臺執行動作處理的Serial Dispatch Queue的生成方法如
testConcurrentQueue方法中第4點
2>dispatch_set_target_queue(parameter1,parameter2)
paramter1:要變更執行優先級的Dispathc Queue
parameter2:要使用的執行優先級相同優先級的Global Dispatch Queue
如果paramter1指定系統提供的Main Dispatch Queue和Global Dispatch Queue則不知道會出現什么狀況,因此這些
均不可指定。
將Dispatch Queue指定dispatch_set_target_queue()的參數,不僅可以變更Dispatch Queue的執行優先級,還可以
作成Dispatch Queue的執行階層。如果在多個Serial Dispatch Queue中用dispatch_set_target_queue()指定目標
為某一個Serial Dispatch Queue,那么原來應并行執行多個Serial Dispatch Queue,在目標Serial Dispatch Queue上
只能同時執行一個處理。
3>在必須將不可并行執行的處理追加到多個Serial Dispatch Queue中時,如果使用dispatch_set_target_queue()將目標
指定為某一個Serial Dispatch Queue,即可防止處理并行執行。
#pragma mark -dispatch_after
//8.dispatch_after
1>經常會有這產的情況,想在3秒后執行處理,可能不僅限于3秒,總之,這種想在指定時間后執行處理的情況,可使用dispatch_after()
來實現
//ull(unsigned long long),NSEC_PER_MSEC以毫秒為單位計算
dispatch_time_t time =dispatch_time(DISPATCH_TIME_NOW,3ull *NSEC_PER_SEC);
dispatch_after(time, mainDispatchQueue, ^{
NSLog(@"waited at least 3 seconds.");
});
注意:
dispatch_after()并不是在指定時間后執行處理,而只是在指定時間追加處理到Dispatch Queue,此源代碼與在3秒后用dispatch_async()
追加Block到Main Dispatch Queue的相同
2>因為Main Dispatch Queue在主線程的RunLoop中執行,所以在比如每隔1/60秒執行的RunLoop中,Block最快在3秒后執行,最慢在3秒+1/60秒
后執行,并且Main Dispatch Queue有大量處理追加或主線程的處理本身有延遲時,這相時間會更長
3> dispatch_after(p1,p2,p3)
p1:指定時間的dispatch_time_t類型的值,該值使用dispatch_time()或dispatch_walltime()作成
p2:指定要追加處理的Dispatch Queu
p3:指定記述要執行處理的Block
dispatch_time()用于計算相對時間
dispatch_walltime()用于計算絕對時間
如用disptch_after()中想指定2016.2.2610h10m11s這一絕對時間的情況,如在上面例子中第6點
#pragma mark -Dispatch Group
//9.Dispatch Group
1>在追加到Dispatch Queue中的多個處理全部結束后想執行處理,這種情況會經常出現,只使用一個Serial Dispatch Queue
時,只要將想執行的處理全部追加到該Serial Dispatch Queue中并在最后追加結束處理即可,但是,在使用
Concurrent Dispatch Queue時或同進使用多個Dispatch Queue時,源代碼就會變得頗為復雜
2>追加3個Block到Global Dispatch Queue,這些Block如果全部執行完畢,就會執行Main Dispatch Queue中結束處理的
Block:
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, globalDispatchQueueDefault, ^{NSLog(@"blk0");});
dispatch_group_async(group, globalDispatchQueueDefault, ^{NSLog(@"blk1");});
dispatch_group_async(group, globalDispatchQueueDefault, ^{NSLog(@"blk2");});
dispatch_group_notify(group, mainDispatchQueue, ^{NSLog(@"done");});
#if !OS_OBJECT_USE_OBJC
dispatch_release(group);
#endif
因為向Global Dispatch Queue即Concurrent Dispatch Queue追加處理,多個線程并行執行,所以
追加處理的執行順序不定。執行時會發生變化,但是此執行結果的done一定是最后輸出的
3>無論向什么樣的Dispatch Queue中追加處理,使用Dispatch Group都可監視這些處理執行的結束,一旦
檢測到所有處理執行結束,就可將結束的處理追加到Dispatch Queue中
4>dispatch_group_async()與dispatch_async()相同,都追加Block到指定的Dispatch Queue,與dispatch_async()
不同的是指定生成的Dispatch Group為第一個參數,指定的Block屬于指定的Dispatch Group。
5>追加Block到Dispatch Queue時同樣,Block通過dispathc_retain()持有Dispatch Group,從
而使得該Block屬于Dispatch Group。這樣如果Block執行結束,該Block就通過dispatch_release()釋放
持有的Dispatch Group,一旦Dispatch Group使用結束,不用考慮屬于該Dispatch Group的Block,立即
通過dispatch_release()釋放即可
6>dispatch_group_notify(p1,p2,p3);
在追加到Dispatch Group中的處理全部執行結束時,該源代碼中使用的dispatch_group_notify()會將執行Block
追加到Dispatch Queue中。
p1,指定要監視的Dispatch Group,在追加到Dispatch Group的全部全部處理執行結束時,將p3的Block追加到p2
的Dispatch Queue中。
在dispatch_group_notify()中不管指定什么樣的Dispatch Queue,屬于Dispatch Group的全部處理在追加指定
的Block時都已執行結束
7>dispatch_group_wait()
另外,在Dispatch Group中也可以使用dispatch_group_wait()僅等全部處理執行結束。
dispatch_group_t group_w = dispatch_group_create();
dispatch_group_async(group_w, globalDispatchQueueDefault, ^{NSLog(@"blk0");});
dispatch_group_async(group_w, globalDispatchQueueDefault, ^{NSLog(@"blk1");});
dispatch_group_async(group_w, globalDispatchQueueDefault, ^{NSLog(@"blk2");});
dispatch_group_wait(group_w, DISPATCH_TIME_FOREVER);
#if !OS_OBJECT_USE_OBJC
dispatch_release(group_w);
#endif
dispatch_group_wait(p1,p2)
p2:指定為等待的時間(超時),它屬于dispatch_time_t類型的值,DISPATCH_TIME_FOREVER永久等待,只要
Dispatch Group的處理尚未執行結束,就會一直等待,中途不能取消。如同dispatch_after()說明中出現的那樣
指定等待間隔為1秒應做如下處理:
time = dispatch_time(DISPATCH_TIME_NOW,1ull *NSEC_PER_SEC);
dispatch_group_t group_result = dispatch_group_create();
long result = dispatch_group_wait(group_result, time);
if (result == 0) {
//屬于Dispatch Group的全部內容處理執行結束
}else{
//屬于Dispatch Group的某一個處理還在執行中
}
如果dispatch_group_wait()返回不是0,即雖然經過了指定的時間,但屬于Dispatch Group的某一個處理還在執行中,如果
返回值為0,那么全部處理執行結束。當等待時間為DISPATCH_TIME_FOREVER,由dispatch_group_wait()返回時,由于屬于
Dispatch Group的處理必定全部執行結束,因此返回值恒為0。
這時的”等待“意味著一旦調用dispatch_group_wait(),該函數就處于調用的狀態而不返回。即執行dispatch_group_wait()的
現在線程(當前線程)停止。在經過dispatch_group_wait()中指定的時間或屬于指定Dispatch Group和處理全部執行結束之前,
執行該函數的線程停止。
指定DISPATCH_TIME_NOW,則不用作何等待即可判定屬于Dispatch Group的處理是否執行結束:
long result = dispatch_group_wait(group_result,DISPATCH_TIME_NOW);
在主線程的RunLoop的每次循環中,可檢查執行是否結束,從而不耗費多余的等待時間,雖然這樣也可以,但一般在這
種情形dispatch_group_notify()追加結束處理到Main Dispatch Queue中,這時因為dispatch_group_notify()
可以簡化源碼
#pragma mark -dispatch_barrier_async
//10.dispatch_barrier_async
1>比如訪問數據庫或文件,為了高效率進行訪問,讀取處理追加到Concurrent Dispatch Queue中,寫入處
理在任一個讀取處時沒有執行的狀態下,追加到Serial Dispatch Queue中即可(在寫入處理結束之前,讀
取處理不可執行),雖然利用Dispatch Group和dispatch_set_target_queue()也可實現,但是源代碼會
很復雜。
2>dispatch_barrier_async()同dispatch_queue_create()生成的Concurrent Dispatch Queue一
起用,首先,dispatch_queue_create()生成Concurrent Dispatch Queue,在dispatch_async中追加
讀處理,在blk2,blk4間加入寫入處理,并將寫入的內容讀到blk4處理以及之后的處理中。
3>dispatch_barrier_async()會等侍追加到Concurrent Dispatch Queue上的并行的處理全部結束之后,再
將指定的處理追加到該Concurrent Dispatch Queue中。然后在由dispatch_barrier_async()追加的處理執行
完畢后,Concurrent Dispatch Queue才恢復為一般的動作,追加到該Concurrent Dispatch Queue的處理又
開始并行執行。
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk0 for reading");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk1 for reading");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk2 for reading");});
dispatch_barrier_async(concurrentDispatchQueue, ^{NSLog(@"blk3 for writing");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk4 for reading");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk5 for reading");});
dispatch_async(concurrentDispatchQueue, ^{NSLog(@"blk6 for reading");});
#pragma mark -dispatch_sync
//11.dispatch_sync
dispatch_async():將指定的Block非同步地追加到指定的Dispatch Queue中,dispatch_async()不
做任何等待。
dispatch_sync():將指定的Block同步地追加到指定的Dispatch Queue中,在追加Block結束之前,
dispatch_sync()會一直等待。
一旦調用dispatch_sync(),那么在指定的處理執行結束之前,該函數不會返回,dispatch_sync()可簡化
源代碼,也可說是簡易版的dispatch_group_wait();
在編程中,最好在深思熟慮、最好希望達到的目的之后再使用dispatch_sync()等同步等待處理執行的API,
因為使用這種API時,稍有不慎就倒導致程序死鎖。
//1>在主線程中執行死鎖:Main Dispatch Queue即主線程中執行指定的Block,并等待執行結束。而其實
在主線程中正在執行這些源代碼,所以無法執行追加到Main Dispatch Queue的Block
dispatch_sync(mainDispatchQueue, ^{NSLog(@"死鎖1");});
//2>Main Dispatch Queue 中執行的Block等待Main Dispatch Queue中要執行的Block執行結束。
dispatch_async(mainDispatchQueue, ^{
dispatch_sync(mainDispatchQueue, ^{NSLog(@"死鎖2");});
});
//3>
dispatch_async(serialDispatchQueue, ^{
dispatch_sync(serialDispatchQueue, ^{NSLog(@"死鎖3");});
});
#pragma mark -dispatch_apply()
//12.dispatch_apply()
1>dispatch_apply()是dispathc_sync()和Dispatch Group的關聯API。該函數按指定的次數將指定的
Block追加到指定的Dispatch Queue中,并等待全部處理執行結束。
dispatch_apply(10, globalDispatchQueueDefault, ^(size_t index) {
NSLog(@"dispatch_apply = %zu",index);
});
NSLog(@"dispatch_apply() done.");
輸出:
dispatch_apply = 4
dispatch_apply = 1
dispatch_apply = 0
...
dispatch_apply() done.
Global Dispatch Queue中執行處理,所以各個處理的執行時間不定,但是輸出結果中最后的done必定在最后
的位置上,這是因為dispatch_apply()會等待全部處理執行結束
2> dispatch_apply(10, globalDispatchQueueDefault, ^(size_t index))
第一個參數是復復次數,第二個參數為追加對象的Dispatch Queue,第三個參數為追加的處理,是帶有參數的Block,
與其他出現的例子不同,這是為了按第一個參數重復追加Block并區分各個Block而使用。
eg:對數組對象的所有元素執行片理是,不必一個一個編寫for循環部分:
NSArray *array = self.datas;
dispatch_apply(self.datas.count, globalDispatchQueueDefault, ^(size_t index){
NSLog(@"index= %zu,element = %@",index,array[index]);
});
3>dispatch_apply()與dispatch_sync()函數相同,會等待處理執行結束,因此推薦在dispatch_async()
中非同步地執行dispatch_apply()
dispatch_async(globalDispatchQueueDefault, ^{
//Global Dispatch Queue 等待dispatch_apply函數中全部處理執行結束
dispatch_apply(self.datas.count, globalDispatchQueueDefault, ^(size_t index){
NSLog(@"index= %zu,element = %@",index,array[index]);
});
//dispatch_apply()中處理全部執行結束
//在Main Dispatch Queue中執行處理用戶界用更新等
dispatch_async(mainDispatchQueue, ^{NSLog(@"在Main Dispatch Queue中執行處理用戶界用更新等...,Done");});
});
#pragma mark -dispatch_suspend()/dispatch_resume()
//13.dispatch_suspend()/dispatch_resume()
當追加大量處理到Dispatch Queue時,在追加處理的過程中,有時希望不執行已追加的處理,如演算結果被
Block截獲時,一些處理會對這個演算結果造成影響。
在這種情況下,只要掛起Dispatch Queue即可,當可以執行時再恢復
掛起指定的Dispatch Queue:
dispatch_suspend(globalDispatchQueueDefault);
恢復指定的Dispatch Queue:
dispatch_resume(globalDispatchQueueDefault);
這些函數對已經執行的處理沒有影響,掛起后,追加到Dispatch Queue中但尚未執行的處理在些之扣停止執行,
而恢復則使用這些處理能夠繼續執行。
#pragma mark -dispatch Semaphore
//14.dispatch Semaphore
當并行執行的處理更新數據時,會產生數據不一致的情況,有時應用程序還會異常結束,雖然使用Serial Dispatch Queue
和dispatch_barrier_async()可避免這類問題,但有必要進行更細粒度的排他控制
1>dispatch Semaphore是持有計數的信號,該計數是多線程編程中的計數類型信號,所謂信號,類似于過馬咱時常
用的手旗,可以通過是舉起手旗,不可通過時放下手旗。而在Dispatch Semaphore中,使用計數來實現該功能。計
數為0時等待,計數為1或大于1時,減去1而不等待。
2>dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);參數表示計數的初始值為1,
從create可以看出,該函數與Dispatch Queue和Dispatch Group一樣,必須通過dispatch_release()
釋放,也可通過dispatch_retain()持有。
3>dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait()等待Dispatch Semaphore的計數值達到大于或等于1。當計數值大于等于1,或
者在待機中計數大于等于1時,對該計數進行減法并從dispatch_semaphore_wait()返回;第二個參數與
dispatch_group_wait()相同,由dispatch_time_t類型值指定等待時間。另外dispatch_semaphore_wait()
的返回值也與dispatch_group_wait()相同。
//Dispatch Semaphore的計數初始值設定為1,保證可訪問的NSArray類的對象的線程同時只能有1個
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//1>
{
NSMutableArray *array = [NSMutableArray new];
for (int i =0; i<100000; i++) {
dispatch_async(globalDispatchQueueDefault, ^{
//等待Dispatch Semaphore,一直等待,直到Dispatch Semaphore的計數值達到大于等于1。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
/*
Dispatch Semaphore的計數值達到大于等于1,所以將Dispatch Semaphore的計數值減去1
dispatch_semaphore_wait()執行返回。即執行到此進的Dispatch Semaphore的計數值恒
為0,由于可訪問NSArray類對象的線程只有1個,因此可安全地進行更新。
*/
[array addObject:@(i)];
/*
排他控制處理結束,所以通過dispatch_semaphore_signal()將Dispatch Semaphore的計
數值加1。如果有通過dispatch_semaphore_wait通過()等待Dispatch Semaphore和計數值
增加的線程,就由最先等待的線程執行。
*/
dispatch_semaphore_signal(semaphore);
});
}
}
#if !OS_OBJECT_USE_OBJC
dispatch_release(semaphore);
#endif
#pragma mark -dispatch_once()
//15.dispatch_once()
dispatch_once()是保證在應用程序執行中只執行一次指定處理的API,下面這種經常出現的用來進行初始化的
源代碼可通過dispatch_once()簡化
static int initialized =NO;
if(initialized == NO){
initialized = YES;
}
如果用dispatch_once(),則源代碼寫為:
static dispatch_once_t pred;
dispatch_once(&pred,^{
initialized = YES;
});
通過dispatch_once(),該源代碼即使在多線程環境下執行,可保證100%安全
之前蝗源代碼在大多數情況下也是安全的,但在多核CPU中,在正在更新表示是否初始化的標志變量時讀取,就有
可能多次執行初始化處理。而用dispatch_once()初始化就不必擔心這樣的問題。
這就是所說的單例模式,在生成單例對象時使用。
#pragma mark -Dispatch I/O
//16.Dispatch I/O
在讀取較大文件時,如果將文件分成合適的大小并使用Global Dispatch Queue并列讀取的話,應該會比一般的
讀取速度快不少。現在的輸入/輸出硬件已經可以做到一次使用多個線程更快地并列讀取了。能實現這一功能的就是
Dispatch I/O和Dispath Data。
通過Dispatch I/O讀寫文件時,使用Global Dispatch Queue將文件按某個大小read/write
如下:
- (void)readByteBySize{
dispatch_queue_t queue = dispatch_queue_create("blog.csdn.com/baitxaps",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{/*讀取 0-8181字節*/});
dispatch_async(queue, ^{/*讀取 8181-15502字節*/});
dispatch_async(queue, ^{/*讀取 15503-23335字節*/});
dispatch_async(queue, ^{/*讀取 23336-555555字節*/});
dispatch_async(queue, ^{/*讀取 555555-65535字節*/});
}
/*
像上面這樣,將文件分割為一塊一塊地進行讀取處理。分割讀取的數據通過使用Dispatch Data可更為簡單地進行結合和分割
蘋果使用Dispatch I/O和Dispath Data。
*/
/*
Apple System Log API 用的源代碼(libc-763.11 gen/asl.c)
*/
- (void)filesReader{
dispatch_queue_t pipe_q = dispatch_queue_create("PipeQ",NULL);
dispatch_fd_t fd;//
//生成Dispatch I/O,發生錯誤時用來執行處理的Block,以及執行該Block的Dispatch Queue
dispatch_io_t pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int error) {
close(fd);
});
//char *out_fd = fdpair[1];
//設定一次讀取的分割大小
dispatch_io_set_low_water(pipe_channel, SIZE_MAX);
dispatch_data_t pipeData;//
//使用Global Dispatch Queue并列讀取
dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t data,int error) {
if (error == 0) {
size_t len = dispatch_data_get_size(pipeData);
if (len>0) {
const char *bytes = NULL;
char *encoede;
dispatch_data_t md = dispatch_data_create_map(pipeData, (constvoid **)&bytes, &len);
// encoede = asl_core_encode_buffef(bytes,len);//
//asl_set((aslmsg)merged_msg,ASL_KEY_AUX_DATA,encoede);
free(encoede);
// _asl_send_message(NULL,merged_msg,-1,NULL);
// asl_msg_release(merged_msg);
#if !OS_OBJECT_USE_OBJC
dispatch_release(md);
#endif
}
}
if (done) {
//dispatch_semaphore_signal(sem);
#if !OS_OBJECT_USE_OBJC
dispatch_release(pipe_channel);
dispatch_release(pipe_q);
#endif
}
});
}
新聞熱點
疑難解答