一. 重點:
1.dispatch_queue_create(生成Dispatch Queue)
2.Main Dispatch Queue/Global Dispatch Queue
3.dispatch_set_target_queue
4.dispatch_after
5.Dispatch Group
6.dispatch_barrier_async
7.dispatch_sync
8.dispatch_apply
9.dispatch_suspend/dispatch_resume
10.Dispatch Semaphore
11.dispatch_once
12.Dispatch I/O
13.spatch_set_context與dispatch_set_finalizer_f
二.內容
#pragma mark -- 1.dispatch_queue_create(生成Dispatch Queue)
/*
1.Dispatch_Queue
兩種:
Serial Dispatch Queue? 串行隊列 順序執行
Concurrent Dispatch Queue 并行隊列 并行執行
dispatch_async(dispatch_queue_t queue>, ^(void)block)
*/
#pragma mark --? 2.Main Dispatch Queue/Global Dispatch Queue
/*
2.Dispatch_queue_creat(生成Dispatch Queue)
生成Serial Queue串行 但將4個它可并行實施多線程更新數據 每一個Searial Queue一個線程
1).生成Serial Queue
dispatch_queue_t queue = dispatch_queue_create("name", NULL);
2).生成Concurrent Queue
dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_CONCURRENT);
create 對應 release (MRC)
dispatch_release(queue);
*/
#pragma mark --? 3.dispatch_set_target_queue
//3.Main Dispatch Queue (串行)/ Global Dispatch Queue(并行) -- (系統的)
//1).Main Dispatch Queue 系統的主線程,串行線程
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//2).Global Dispatch Queue(四種優先級) 不需要retain release
//(1)High Priority
dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//(2)Default Priority
dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//(3)Low Priority
dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//(4)Background Priority
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
//在Main Dispatch Queue和Global Dispatch Queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 可并行執行處理
// 在Main Dispatch Queue中執行Block
dispatch_async(dispatch_get_main_queue(), ^{
// 主線程中執行處理
});
});
//數據的處理都放到并行線程中,對于UI的修改,要放到主線程中.
//4.dispatch_set_target_queue(變更生成的Dispatch Queue優先級)
dispatch_queue_t queue = dispatch_queue_create("name", NULL);
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 使生成的queue優先級與globalQueue相同
dispatch_set_target_queue(queue, globalQueue);
//層次管理
//Dispatch Queue 如果多個Serial Dispatch Queue使用該函數指定為目標為某一個Serial Dispatch Queue
#pragma mark --? 4.dispatch_after
//dispatch_after:是延遲提交,不是延遲運行, 同時并不精確
//官方文檔說明:Enqueue a block for execution at the specified time.
//Enqueue,就是入隊,指的就是將一個Block在特定的延時以后,加入到指定的隊列中,不是在特定的時間后立即運行!。
//方法一
/*
#define NSEC_PER_SEC 1000000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
- NSEC_PER_SEC,每秒有多少納秒。
- USEC_PER_SEC,每秒有多少毫秒。(注意是指在納秒的基礎上)
- NSEC_PER_USEC,每毫秒有多少納秒。
關鍵詞解釋:
- NSEC:納秒。
- USEC:微妙。
- SEC:秒
- PER:每
*/
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"處理事情");
});
//方法二
/*
dispatch_after 并不是在指定時間后執行處理,而是在指定質檢追加處理到Dispatch Queue于3秒后執行,dispatch_async()函數追到Block到Main Dispatch Queue
參數1:dispatch_time類型,使用dispatch_time函數和dispatch_walltime函數生成,dispatch_time類型值中指定的時間開始;dispatch_walltime計算絕對時間(固定時間)
參數2:指定要追加處理Dispatch Queue
參數3:指定要記述執行處理的Block
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"處理事情");
});
//注意:
//創建串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_SERIAL);
//立即打印一條信息
NSLog(@"開始添加block");
//提交一個block
dispatch_async(serialQueue, ^{
//讓線程沉睡10s
[NSThread sleepForTimeInterval:10];
NSLog(@"第一個block完成");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)),serialQueue, ^{
NSLog(@"Afterblock完成");
});
/*
結果是: 開始添加block ->? 第一個block完成 ->? Afterblock完成
從結果也驗證了,dispatch_after只是延時提交block,并不是延時后立即執行。所以想用dispatch_after精確控制運行狀態的朋友可要注意了~
*/
#pragma mark --? 5.Dispatch Group 全部結束后想執行結束處理
//1) 三個事件異步執行,done在三個事件都完成之后,才會調用.
dispatch_group_t groupQueue = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_async(groupQueue, queue, ^{
NSLog(@"1");
});
dispatch_group_async(groupQueue, queue, ^{
NSLog(@"2");
});
dispatch_group_async(groupQueue, queue, ^{
NSLog(@"3");
});
dispatch_group_notify(groupQueue, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
//2)等待其中并行處理多長時間之后,就執行
dispatch_group_t groupQueueTwo = dispatch_group_create();
dispatch_queue_t queueTwo = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_async(groupQueueTwo, queueTwo, ^{
NSLog(@"4");
});
dispatch_group_async(groupQueueTwo, queueTwo, ^{
NSLog(@"5");
});
dispatch_group_async(groupQueueTwo, queueTwo, ^{
NSLog(@"6");
});
//第二個參數指定為等待時間(超時)dispatch_time_t類型的值,上面用的是一直等待。
dispatch_group_wait(groupQueueTwo, DISPATCH_TIME_FOREVER);
dispatch_time_t queueTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
long result = dispatch_group_wait(groupQueueTwo, queueTime);
if (!result) {
NSLog(@"屬于Dispatch Group全部處理執行結束");
}else
{
NSLog(@"屬于Dispatch Group的某一個處理還在執行");
}
/*
不為0意味著雖然過了指定時間,但屬于Dispatch Group的某一個處理還在執行中,為0全部處理執行結束
指定DISPATCH_TIME_NOW,則不用任何等待即可判定屬于Dispatch Group的處理是否結束,long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
當你無法直接使用隊列變量時,就無法使用dispatch_group_async了,也可以使用dispatch_group_enter和dispatch_group_leave來作為判斷group結束的標志,下面以使用AFNetworking時的情況:
同時處理多個網絡請求,要求在所有請求都結束的時候,刷新UI,使用dispatch_group_enter,dispatch_group_leave就可以方便的將一系列網絡請求“打包”起來~
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//Enter group
dispatch_group_enter(group);
[manager GET:@"http://www.baidu.com" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Deal with result...
//Leave group
dispatch_group_leave(group);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Deal with error...
//Leave group
dispatch_group_leave(group);
}];
//More request...
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
更新UI
});
*/
#pragma mark --? 6.dispatch_barrier_async 用來加鎖,防止數據源沖突
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(queue, ^{
NSLog(@"11");
});
dispatch_async(queue, ^{
NSLog(@"12");
});
dispatch_async(queue, ^{
NSLog(@"13");
});
//執行barrier完成之后才繼續執行
dispatch_barrier_async(queue, ^{
NSLog(@"barrier done");
});
dispatch_async(queue, ^{
NSLog(@"14");
});
/*
dispatch_barrier_async的作用就是向某個隊列插入一個block,當目前正在執行的block運行完成后,阻塞這個block后面添加的block,只運行這個block直到完成,然后再繼續后續的任務,有點“唯我獨尊”的感覺=。=
值得注意的是:
dispatchbarrier\(a)sync只在自己創建的并發隊列上有效,在全局(Global)并發隊列、串行隊列上,效果跟dispatch_(a)sync效果一樣。
既然在串行隊列上跟dispatch_(a)sync效果一樣,那就要小心別死鎖!
dispatch_barrier_sync(dispatch_get_main_queue(), ^{
NSLog(@"我是死鎖");
});
*/
#pragma mark --? 7.dispatch_sync 同步(將Block同步到Dispatch Queue)中,在追加Block結束之前,dispatch_sync會一直等待(當前線程提醒)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
// 處理結束前不會返回
dispatch_sync(queue, ^{
NSLog(@"done");
});
//dispatch_sync導致的死鎖
//涉及到多線程的時候,不可避免的就會有“死鎖”這個問題,在使用GCD時,往往一不小心,就可能造成死鎖,看看下面的“死鎖”例子:
/*
//在main線程使用“同步”方法提交Block,必定會死鎖。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"I am block...");
});
*/
#pragma mark --? 8.dispatch_apply? 等待處理結果執行完才進行,關聯dispatch_sync函數和Dispatch Group的關聯API
/*
第一個參數為重復次數
第二個參數為追加對象的Dispatch Queue
第三個參數為追加處理 帶參數Block,為了區別重復Block
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zu",index);
});
//明明是提交到異步的隊列去運行,但是“After apply”居然在apply后打印,也就是說,dispatch_apply將外面的線程(main線程)“阻塞”了!
//查看官方文檔,dispatch_apply確實會“等待”其所有的循環運行完畢才往下執行=。=,看來要小心使用了。
/*
//在apply中使用主線程,會產生相互等待的死鎖
dispatch_apply(10, dispatch_get_main_queue(), ^(size_t index) {
NSLog(@"%zu",index);
});
避免dispatch_apply的嵌套調用,否則也會產生死鎖.
dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"apply loop: %zu", i);
//再來一個dispatch_apply!死鎖!
dispatch_apply(3, queue, ^(size_t j) {
NSLog(@"apply loop inside %zu", j);
});
});
*/
NSLog(@"apply完成");
#pragma mark --? 9.dispatch_suspend/dispatch_resume
/*
掛起函數:dispatch_suspend(queue); 被追加到Dispatch Queue中的尚未執行的處理停止
回復函數:dispatch_resume(queue)p; 繼續執行
dispatch_suspend != 立即停止隊列的運行
dispatch_suspend,dispatch_resume提供了“掛起、恢復”隊列的功能,簡單來說,就是可以暫停、恢復隊列上的任務。但是這里的“掛起”,并不能保證可以立即停止隊列上正在運行的block,看如下例子:
*/
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//提交第一個block 延時5s打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"After 5 seconds...");
});
//提交第二個block,也是延時5秒打印
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:5];
NSLog(@"After 5 seconds again...");
});
//延時一秒
NSLog(@"sleep 1 second...");
[NSThread sleepForTimeInterval:1];
//掛起隊列
NSLog(@"suspend...");
dispatch_suspend(queue);
//延時10秒
NSLog(@"sleep 10 second...");
[NSThread sleepForTimeInterval:10];
//恢復隊列
NSLog(@"resume...");
dispatch_resume(queue);
/*
2016-06-15 22:30:15.905 FJSGCDDemo[20425:462101] sleep 1 second...
2016-06-15 22:30:16.906 FJSGCDDemo[20425:462101] suspend...
2016-06-15 22:30:16.906 FJSGCDDemo[20425:462101] sleep 10 second...
2016-06-15 22:30:20.907 FJSGCDDemo[20425:462215] After 5 seconds...
2016-06-15 22:30:26.908 FJSGCDDemo[20425:462101] resume...
2016-06-15 22:30:31.913 FJSGCDDemo[20425:462143] After 5 seconds again...
可知,在dispatch_suspend掛起隊列后,第一個block還是在運行,并且正常輸出。
結合文檔,我們可以得知,dispatch_suspend并不會立即暫停正在運行的block,而是在當前block執行完成后,暫停后續的block執行。
所以下次想暫停正在隊列上運行的block時,還是不要用dispatch_suspend了吧~
*/
#pragma mark --? 10.Dispatch Semaphore 暫停,播放
/*
10.Dispatch Semaphore:暫停,播放
持有計數的信號,該計數是多線程中的計數類型信號,信號類似于過馬路手旗,可以通過舉起手旗,不可通過時放下手旗,計數為0時等待,計數為1或大于1時,減去1而不等待
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (NSInteger i = 0; i < 10000; i++) {
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 進行排他處理,計數值+1進行
if (i == 500) {
dispatch_semaphore_signal(semaphore);
NSLog(@"我這個時候才能處理事情");
}
NSLog(@"難道我處理不了事情?");
});
}
#pragma mark --? 11.dispatch_once
//dispatch_once_t必須是全局或static變量
//這一條算是“老生常談”了,但我認為還是有必要強調一次,畢竟非全局或非static的dispatch_once_t變量在使用時會導致非常不好排查的bug,正確的如下:
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 初始化,一般用于單例中
});
#pragma mark --? 12.Dispatch I/O
//將文件分割成一塊一塊進行讀取
#pragma mark --? 13.spatch_set_context與dispatch_set_finalizer_f
/*
dispatch_set_context可以為隊列添加上下文數據,但是因為GCD是C語言接口形式的,所以其context參數類型是“void *”。也就是說,我們創建context時有如下幾種選擇:
用C語言的malloc創建context數據。
用C++的new創建類對象。
用Objective-C的對象,但是要用__bridge等關鍵字轉為Core Foundation對象。
以上所有創建context的方法都有一個必須的要求,就是都要釋放內存!,無論是用free、delete還是CF的CFRelease,我們都要確保在隊列不用的時候,釋放context的內存,否則就會造成內存泄露。
所以,使用dispatch_set_context的時候,最好結合dispatch_set_finalizer_f使用,為隊列設置“析構函數”,在這個函數里面釋放內存,大致如下:
void cleanStaff(void *context) {
//釋放context的內存!
//CFRelease(context);
//free(context);
//delete context;
}
...
//在隊列創建后,設置其“析構函數”
dispatch_set_finalizer_f(queue, cleanStaff);
*/
附上github地址https://github.com/BestJoker/FJSGCDDemo.git,有需要的朋友可以去下載,別忘了給我個星星 - -,有問題可以隨時留言.