-
Managing Units of Work(管理工作單位)
調度塊允許您直接配置隊列中各個工作單元的屬性。它們還允許您處理個別工作單位,以等待其完成,并通知其完成和/或取消它們
-
dispatch_block_t
1.這是提交給dispatch隊列的塊的原型,它不帶參數,也沒有返回值。
2.創建block的方式有兩種
-
2.1:dispatch_block_create
2.1.1
-
2.2:dispatch_block_create_with_qos_class
dispatch_block_t blockT=dispatch_block_create(0, ^{
NSLog(@"11111");
});
NSLog(@"22222");
dispatch_queue_t myQueue=dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(myQueue, blockT);
NSLog(@"33333");
//第三個參數設置成1 居然在下面dispatch_async(myQueue, blockY);處奔潰,不知道是不是死鎖,暫不知原因
dispatch_block_t blockY=dispatch_block_create_with_qos_class(1, QOS_CLASS_USER_INTERACTIVE, 0, ^{
NSLog(@"44444");
});
dispatch_async(myQueue, blockY);
NSLog(@"5555");
參數:
參數1:是一個dispatch_block_flags枚舉
DISPATCH_BLOCK_BARRIER
DISPATCH_BLOCK_DETACHED
DISPATCH_BLOCK_ASSIGN_CURRENT
DISPATCH_BLOCK_NO_QOS_CLASS
DISPATCH_BLOCK_INHERIT_QOS_CLASS
DISPATCH_BLOCK_ENFORCE_QOS_CLASS
并不太清楚具體原因
參數2.而 qos_class_t 是一種枚舉,有以下類型:
QOS_CLASS_USER_INTERACTIVE: user interactive 等級表示任務需要被立即執行,用來在響應事件之后更新 UI,來提供好的用戶體驗。這個等級最好保持小規模。
QOS_CLASS_USER_INITIATED: user initiated 等級表示任務由 UI 發起異步執行。適用場景是需要及時結果同時又可以繼續交互的時候。
QOS_CLASS_DEFAULT: default 默認優先級
QOS_CLASS_UTILITY: utility 等級表示需要長時間運行的任務,伴有用戶可見進度指示器。經常會用來做計算,I/O,網絡,持續的數據填充等任務。這個任務節能。
QOS_CLASS_BACKGROUND: background 等級表示用戶不會察覺的任務,使用它來處理預加載,或者不需要用戶交互和對時間不敏感的任務。
QOS_CLASS_UNSPECIFIED: unspecified 未指明
參數3:
-
dispatch_function_t
1.提交調度隊列的函數原型
2.函數可以接受一個dispatch_function_t類型作為參數,同時接受一個指向您提供上下文數據。調用調度函數時,將上下文數據的指針作為參數傳遞給函數。對上下文數據的指針未經修改而傳遞給您的函數,您有責任確保指針是有效的
-
dispatch_block_perform
1.從指定的塊和標志創建、同步執行和釋放調度塊
2.在不需要對指定塊的堆或一個新塊對象的分配進行復制的情況下,可以更有效地實現此功能
3.相當于如下代碼
//調用
NSLog(@"11111");
dispatch_block_perform(0, ^{
NSLog(@"2222");
});
NSLog(@"3333");
//dispatch_block_perform相當于如下
dispatch_block_t b = dispatch_block_create(flags, block);
b();
Block_release(b)
-
dispatch_block_wait
1.同步地等待,直到執行指定的調度塊已經完成,或者直到指定的超時已經過去。
2.該函數會阻塞當前線程進行等待。傳入需要設置的 block 和等待時間 timeout 。timeout 參數表示函數在等待 block 執行完畢時,應該等待多久。如果執行 block 所需的時間小于 timeout ,則返回 0,否則返回非 0 值。此參數也可以取常量DISPATCH_TIME_FOREVER ,這表示函數會一直等待 block 執行完,而不會超時??梢允褂?dispatch_time 函數和 DISPATCH_TIME_NOW 常量來方便的設置具體的超時時間。
3.如果 block 執行完成, dispatch_block_wait 就會立即返回。不能使用 dispatch_block_wait 來等待同一個 block 的多次執行全部結束;這種情況可以考慮使用dispatch_group_wait 來解決。也不能在多個線程中,同時等待同一個 block 的結束。同一個 block 只能執行一次,被等待一次。
4.注意:因為 dispatch_block_wait 會阻塞當前線程,所以不應該放在主線程中調用
dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQuene, ^{
dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"開始執行");
[NSThread sleepForTimeInterval:3];
NSLog(@"結束執行");
});
dispatch_async(allTasksQueue, block);
// 等待時長,10s 之后超時
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
long resutl = dispatch_block_wait(block, timeout);
if (resutl == 0) {
NSLog(@"執行成功");
} else {
NSLog(@"執行超時");
}
});
-
dispatch_block_notify
1.可理解為監聽
2.在指定的調度塊執行完成時,調度一個通知塊提交給隊列
3.該函數接收三個參數,第一個參數是需要監視的 block,第二個參數是監聽的 block 執行結束之后要提交執行的隊列 queue,第三個參數是待加入到隊列中的 block。 和 dispatch_block_wait 的不同之處在于:dispatch_block_notify 函數不會阻塞當前線程
-
dispatch_block_cancel
1.這個函數用異步的方式取消指定的 block
dispatch_queue_t myQueue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
// 耗時任務
dispatch_block_t firstBlock = dispatch_block_create(0, ^{
NSLog(@"開始第一個任務:%d",[NSThread isMainThread]);
[NSThread sleepForTimeInterval:1.5f];
NSLog(@"結束第一個任務");
});
// 耗時任務
dispatch_block_t secBlock = dispatch_block_create(0, ^{
NSLog(@"開始第二個任務:%d",[NSThread isMainThread]);
[NSThread sleepForTimeInterval:2.f];
NSLog(@"結束第二個任務");
});
dispatch_async(myQueue, firstBlock);
dispatch_async(myQueue, secBlock);
// 等待 1s,讓第一個任務開始運行,因為myQueue是串行隊列,遵守fifo先進先出的規則,所以必須先執行完block1,才能執行block2
[NSThread sleepForTimeInterval:1];
NSLog(@"休眠:%d",[NSThread isMainThread]);
dispatch_block_cancel(firstBlock);
NSLog(@"準備取消第一個任務");
dispatch_block_cancel(secBlock);
NSLog(@"準備取消第二個任務");
/*
打印的結果為:
2017-07-06 18:32:04.046 多線程-GCD[6427:205689] 開始第一個任務:0
2017-07-06 18:32:05.047 多線程-GCD[6427:205642] 休眠:1
2017-07-06 18:32:05.047 多線程-GCD[6427:205642] 準備取消第一個任務
2017-07-06 18:32:05.047 多線程-GCD[6427:205642] 準備取消第二個任務
2017-07-06 18:32:05.547 多線程-GCD[6427:205689] 結束第一個任務
可見 dispatch_block_cancel 對已經在執行的任務不起作用,只能取消尚未執行的任務
*/
-
dispatch_block_testcancel
1.測試給定的調度塊是否已被取消
2.如果取消調度塊,則返回一個非零值,否則為零
-
Prioritizing Work and Specifying Quality of Service(優先工作和指定服務質量
)
-
Dispatch Queue Priorities
1.用于選擇適當的全局并發隊列(global concurrent queue)
1.1 DISPATCH_QUEUE_PRIORITY_HIGH:這個常數映射到 QOS_CLASS_USER_INITIATED類
發送到隊列的項目以高優先級運行;隊列在任何默認優先級或低優先級隊列之前調度執行
1.2 DISPATCH_QUEUE_PRIORITY_DEFAULT:這個常數映射到 QOS_CLASS_DEFAULT類
發送到隊列的項目以默認優先級運行;隊列在所有高優先級隊列已排定之后執行調度,但在調度任何低優先級隊列之前
1.3 DISPATCH_QUEUE_PRIORITY_LOW:這個常數映射到 QOS_CLASS_UTILITY類
發送到隊列的項目以低優先級運行;隊列在所有默認優先級和高優先級隊列已排定之后執行調度
1.4 DISPATCH_QUEUE_PRIORITY_BACKGROUND:這個常數映射到QOS_CLASS_BACKGROUND類
發送到隊列的項目以后臺優先級運行;隊列在所有高優先級隊列被調度后執行調度,系統在其優先級為后臺狀態設置的線程上運行項目。這樣一個線程的優先級最低,任何磁盤I/O是節流,減少對系統的影響
2.在MacOS 10.10以后,利用QoS類代替。有關更多信息,請參見服務質量類(QoS)
-
dispatch_qos_class_t
-
dispatch_queue_priority_t
-
dispatch_queue_get_qos_class
-
dispatch_queue_attr_make_with_qos_class
如上這幾個,官方文檔并沒有做過多介紹,了解就行
-
Using Dispatch Groups(利用調度組)
分組塊允許聚合同步。您的應用程序可以提交多個塊,并在它們全部完成時跟蹤它們,即使它們可能在不同的隊列上運行。當所有指定的任務都完成后,你再需要做什么行為時,這種類是有幫助的
-
dispatch_group_t
1.提交一組塊對象給隊做為異步調用
2.調度組是監視一組塊的機制。應用程序可以根據需要同步或異步監視組中的塊。通過擴展,一個組可以用于對依賴于其他任務完成的代碼進行同步。
注意,組中的塊可以在不同的隊列上運行,并且每個單獨的塊可以向組添加更多的塊
-
dispatch_group_async
1.將block(任務)提交到指定的隊列中,并且將次任務放到(關聯)指定的group,block將異步執行
2.參數
group:要關聯的組。該組由系統保留,直到該塊運行完成為止。此參數不能為空
queue:為異步調用提交塊對象的調度隊列。隊列被系統保留,直到該塊運行到完成為止。此參數不能為空
block:異步執行的塊對象。這個函數執行代表調用者的block_copy和block_release
-
dispatch_group_async_f
1.這個方法跟dispatch_group_async差不多,只是第三個參數為c語言函數
-
dispatch_group_notify_f
1.當group中的任務都完成以后會執行block.注意這句代碼要加到所有任務提交之后才管用.參數queue代表block會提交到哪個隊列中
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t conCurrentQueue = dispatch_queue_create("conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, serialQueue, ^{
NSLog(@"串行隊列任務一開始");
[NSThread sleepForTimeInterval:2];
NSLog(@"串行隊列任務一快結束:%@",[NSThread currentThread]);
});
dispatch_group_async(group, serialQueue, ^{
NSLog(@"串行隊列任務二開始");
[NSThread sleepForTimeInterval:2];
NSLog(@"串行隊列任務二快結束:%@",[NSThread currentThread]);
});
dispatch_group_async(group, conCurrentQueue, ^{
NSLog(@"并行隊列任務二開始");
[NSThread sleepForTimeInterval:2];
NSLog(@"并行隊列任務二快結束:%@",[NSThread currentThread]);
});
dispatch_group_notify(group, conCurrentQueue, ^{
NSLog(@"被通知的并行隊列任務三");
});
//結果時“被通知的并行隊列任務三”是在所有任務都執行完成后才執行
-
dispatch_group_wait
1.為先前提交的塊對象同步地等待完成;如果在指定的超時時間結束之前塊沒有完成,則返回
2.同步等待會阻塞線程,跟dispatch_block_wait原理一樣
dispatch_queue_t conCurrentQueue = dispatch_queue_create("conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, conCurrentQueue, ^{
NSLog(@"任務一開始");
[NSThread sleepForTimeInterval:2];
NSLog(@"任務一快結束");
});
dispatch_group_async(group, conCurrentQueue, ^{
NSLog(@"任務二開始");
[NSThread sleepForTimeInterval:6];
NSLog(@"任務二快結束");
});
dispatch_time_t time=dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
long result=dispatch_group_wait(group, time);
if (result==0) {
NSLog(@"組的block全部執行完成");
}
else{
NSLog(@"組的block沒有全部執行完成,是timeout返回");
}
NSLog(@"-----------");
-
dispatch_group_enter
1.用這個方法指定一個操作將要加到group中,用來替代dispatch_group_async
,注意它只能和dispatch_group_leave
配對使用.
2.這種方式比dispatch_group_async更加靈活.比如我們可以在任務的完成回調里面寫dispatch_group_leave()
dispatch_queue_t conCurrentQueue = dispatch_queue_create("conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(conCurrentQueue, ^{
NSLog(@"任務一開始");
[NSThread sleepForTimeInterval:2];
NSLog(@"任務一快結束");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(conCurrentQueue, ^{
NSLog(@"任務二開始");
[NSThread sleepForTimeInterval:2];
NSLog(@"任務二快結束");
dispatch_group_leave(group);
});
dispatch_group_notify(group, conCurrentQueue, ^{
NSLog(@"被通知任務開始");
});
NSLog(@"-----");
-
Using Dispatch Semaphores(利用調度信號量)
關于信號量的解釋,借別人的一個例子:
停車場剩余4個車位,那么即使同時來了四輛車也能停的下。如果此時來了五輛車,那么就有一輛需要等待。信號量的值就相當于剩余車位的數目,dispatch_semaphore_wait函數就相當于來了一輛車,dispatch_semaphore_signal。就相當于走了一輛車。停車位的剩余數目在初始化的時候就已經指明了(dispatch_semaphore_create(value:Int))),調用一次dispatch_semaphore_signal,剩余的車位就增加一個;調用一次dispatch_semaphore_wait剩余車位就減少一個;當剩余車位為0時,再來車(即調用dispatch_semaphore_wait)就只能等待。有可能同時有幾輛車等待一個停車位。有些車主。沒有耐心,給自己設定了一段等待時間,這段時間內等不到停車位就走了,如果等到了就開進去停車。而有些車主就像把車停在這,所以就一直等下去
-
dispatch_semaphore_create
- 該函數使用一個初始值創建一個dispatch_semaphore_t類型的信號量,注意:這里的傳入的參數value必須大于或等于0,否則dispatch_semaphore_create會返回NULL
2.參數就是信號量的初始值
-
dispatch_semaphore_wait
1.等待信號量,該函數會使傳入的信號量dsema的值減1
2.函數的作用:如果dsema信號量的值大于0,該函數所處線程就繼續執行下面的語句,并且將信號量的值減1;如果desema的值為0,那么這個函數就阻塞當前線程等待timeout(注意timeout的類型為dispatch_time_t,需要傳入對應的類型參數),如果等待的期間desema的值被dispatch_semaphore_signal函數加1了,且該函數(即dispatch_semaphore_wait)所處線程獲得了信號量,那么就繼續向下執行并將信號量減1, 如果等待期間沒有獲取到信號量或者信號量的值一直為0,那么等到timeout時,其所處線程自動執行其后語句。
-
dispatch_semaphore_signal
1.當返回值為0時表示當前并沒有線程等待其處理的信號量,其處理的信號量的值加1即可。當返回值不為0時,表示其當前有(一個或多個)線程等待其處理的信號量,并且該函數喚醒了一個等待的線程(當線程有優先級時,喚醒優先級最高的線程;否則隨機喚醒.
-
關于信號量的用途和總結
0.注意,正常的使用順序是先降低然后再提高,這兩個dispatch_semaphore_wait 和dispatch_semaphore_signal函數通常成對使用
1.如果初始化信號量為1的話,相當于給線程加鎖,因為信號量為1,不管什么情況下,都會保證只有一個線程再訪問此關鍵代碼,是線程安全
2.可以相當于設置最大并發數量,比如初始化的信號量為4,就算有五個線程來了,最多的情況也只會有四個同時進行
3.加鎖
如上所示,crash的原因翻譯過來是:就是說你malloc分配的內存賦值給了一個已經被釋放的指針(此指針已不存在),其實我暫時不明白為什么會這樣報錯,我的理解是也是因為多線程同時寫數組的的問題
修改成如下就行
dispatch_queue_t myQueue = dispatch_queue_create("MyQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray * array = [[NSMutableArray alloc] init];
for (int i =1; i<100; i++) {
dispatch_async(myQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[array addObject:[NSString stringWithFormat:@"%d",i]];
dispatch_semaphore_signal(semaphore);
});
}
4.設置多線程最大并發數
//crate的value表示,最多幾個資源可訪問
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務1
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
//任務2
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
//任務3
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
/*
2017-07-07 17:07:41.632 多線程-GCD[1125:163408] run task 1
2017-07-07 17:07:41.632 多線程-GCD[1125:163421] run task 2
2017-07-07 17:07:42.634 多線程-GCD[1125:163408] complete task 1
2017-07-07 17:07:42.635 多線程-GCD[1125:163421] complete task 2
2017-07-07 17:07:42.635 多線程-GCD[1125:163405] run task 3
2017-07-07 17:07:43.640 多線程-GCD[1125:163405] complete task 3
總結:由于設定的信號值為2,先執行兩個線程,等執行完一個,才會繼續執行下一個,保證同一時間執行的線程數不超過2
這也相當于設置了多線程的最大并發數
*/
-
Using Dispatch Barriers(柵欄)
對于柵欄的理解:假設我們原先有10個任務要執行,我們現在要插入一個任務5,這個任務5要在1,2,3,4,都并發執行完了之后才能執行,而6,7,8,9,10號任務要在這個任務5結束后才允許并發。這是就需要用到Barriers,當然group也可以實現,但是這個方法更好,關于柵欄的只能是同一個隊列,而group 可以是不同隊列,柵欄最好別用系統的全部隊列而是使用自己的全部隊列
-
dispatch_barrier_async
1.提交異步執行的障礙塊并立即返回,不會阻塞線程
2.該方法的功能跟dispatch_async類似
-
dispatch_barrier_async_f
1.跟dispatch_barrier_async方法一樣,只會提交的是c函數
-
dispatch_barrier_sync
1.提交一個用于執行的屏障塊對象,并等待該塊完成
2.該方法功能跟dispatch_sync類似
-
dispatch_barrier_sync_f
1.跟dispatch_barrier_async方法一樣,只會提交的是c函數
-
Managing Dispatch Sources
這個管理類對于我而言還是很少用到的,不過有個確實經常用到的就是 time