背景
擔心了兩週的我終於輪到去醫院做胃鏡檢查了!去的時候我都想好了最壞的可能(胃癌),之前在網上查的症狀都很相似。最後檢查結果出來終於安心了,診斷結果:慢性非萎縮性胃炎(胃竇為主)
我是一個心裡素質不過關的人,所以說對待問題的時候可能會有一種悲觀的想法。朋友說我本來可能沒病都被自己嚇出病了,這是一個心態問題。
你們可能問我做胃鏡什麼感覺?我只能告訴你一個字:真爽,具體只能自己去感受。
保持樂觀的心態
看完下面的笑話就要開始我們的裝逼之旅了^_^
- 一個大學生去公司實習,老闆讓他先從掃地開始。大學生:“我可是大學生哎……”老闆:“哦,對了,我差點忘了你是大學生,來來來,我教你怎麼掃地”
- 一天,老師讓同學們寫作文,題目是 《我的理想》。
小明在作文裡寫道:我長大了要去搶銀行,然後把錢分給窮苦老百姓。
第二天老師改完了,寫給小明的評語是這樣的:很不錯的理想,分錢的時候不要忘了老師,但你要注意你的同桌,他說他長大了要去當警察。
GCD基本介紹
- GCD(Grand Central Dispatch)iOS 4.0開始引入的新多執行緒程式設計功能,
- GCD(Grand Central Dispatch)是非同步執行任務的技術之一。一般將應用程式中記述的執行緒管理用的程式碼在系統級中實現。開發者只需要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的執行緒並計劃執行任務。
- GCD(Grand Central Dispatch)是基於C語言開發的一套多執行緒開發機制,是完全程式導向的。
GCD基本概念
這就需要上一篇部落格裡的基本知識了(不清楚去看下)iOS開發之多執行緒程式設計總結(一)
任務和佇列
- 任務:就是執行操作的意思,換句話說就是你線上程中執行的那段程式碼。在GCD中是放在block中的。執行任務有兩種方式:同步執行和非同步執行。兩者的主要區別是:是否具備開啟新執行緒的能力。
- 同步執行(sync):只能在當前執行緒中執行任務,不具備開啟新執行緒的能力
- 必須等待當前語句執行完畢,才會執行下一條語句
- 不會開啟執行緒
- 在當前主執行緒執行 block 的任務
dispatch_sync(queue, block);
- 非同步執行(async):可以在新的執行緒中執行任務,具備開啟新執行緒的能力
- 不用等待當前語句執行完畢,就可以執行下一條語句
- 會開啟執行緒執行 block 的任務
- 非同步是多執行緒的代名詞
dispatch_async(queue, block);
- 同步執行(sync):只能在當前執行緒中執行任務,不具備開啟新執行緒的能力
- 佇列:這裡的佇列指任務佇列,即用來存放任務的佇列。佇列是一種特殊的線性表,採用FIFO(先進先出)的原則,即新任務總是被插入到佇列的末尾,而讀取任務的時候總是從佇列的頭部開始讀取。每讀取一個任務,則從佇列中釋放一個任務。在GCD中有四種佇列:序列佇列、併發佇列、主佇列、全域性佇列。
- 序列佇列(Serial Dispatch Queue):讓任務一個接著一個地執行(一個任務執行完畢後,再執行下一個任務)
- 一次只能”排程”一個任務
dispatch_queue_create("queue", NULL);
或者dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
- 併發佇列(Concurrent Dispatch Queue):可以讓多個任務併發(同時)執行(自動開啟多個執行緒同時執行任務),
- 一次可以”排程”多個任務
- 併發功能只有在非同步(dispatch_async)函式下才有效
dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
- 主佇列
- 專門用來在主執行緒上排程任務的佇列
- 不會開啟執行緒
- 在主執行緒空閒時才會排程佇列中的任務在主執行緒執行
- dispatch_get_main_queue();
- 全域性佇列
- 執行過程和併發佇列一致,參考併發佇列
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
- 序列佇列(Serial Dispatch Queue):讓任務一個接著一個地執行(一個任務執行完畢後,再執行下一個任務)
小結: 在以後的使用中,記住下面的就可以了!
- 開不開執行緒由執行任務的函式決定
- 非同步開,非同步是多執行緒的代名詞
- 同步不開
- 開幾條執行緒由佇列決定
- 序列佇列開一條執行緒(GCD會開一條,NSOperation Queue最大併發數為1時也可能開多條)
- 併發佇列開多條執行緒,具體能開的執行緒數量由底層執行緒池決定
GCD的使用
今天我們學習下面圖片的相關知識點,Demo下載連結會在文章最後給出來
簡單來看一段程式碼:非同步下載圖片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#pragma mark - 1.非同步下載圖片 - (IBAction)downLoadAction:(UIButton *)sender { self.imageView.image = nil; //獲取全域性佇列 dispatch_async(dispatch_get_global_queue(0, 0), ^{ //非同步下載圖片 NSURL *url=[NSURL URLWithString:@"https://p1.bpimg.com/524586/475bc82ff016054ds.jpg"]; //將資源轉換為二進位制 NSData *data=[NSData dataWithContentsOfURL:url]; //將二進位制轉化為圖片 UIImage *image=[UIImage imageWithData:data]; //獲取主佇列,更新UI dispatch_async(dispatch_get_main_queue(), ^{ //給圖片控制元件賦值 self.imageView.image=image; }); }); } |
和NSThread對比可以發現
- 所有的程式碼寫在一起的,讓程式碼更加簡單,易於閱讀和維護
- NSThread 通過 @selector 指定要執行的方法,程式碼分散
- GCD 通過 block 指定要執行的程式碼,程式碼集中
- 使用 GCD 不需要管理執行緒的建立/銷燬/複用的過程!程式設計師不用關心執行緒的生命週期
- 如果要開多個執行緒 NSThread 必須例項化多個執行緒物件或者使用分類方法
- NSThread 靠 NSObject 的分類方法實現的執行緒間通訊,GCD 靠 block
dispatch_async(queue, block);
就是非同步執行一個佇列裡面的任務block。每個block之間是非同步執行的,但是block裡面的程式碼是順序執行的!dispatch_sync(queue, block);
就是同步執行一個佇列裡面的任務block
1. 序列佇列(Serial Dispatch Queue)
序列佇列的建立:
1 2 |
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue = dispatch_queue_create("queue", NULL); |
序列佇列同步和非同步執行Demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#pragma mark - 序列佇列同步和序列佇列非同步 //序列佇列同步 - (void)serialQueueSyncMethod{ //建立佇列 dispatch_queue_t queue = dispatch_queue_create("serialQueueSyncMethod", DISPATCH_QUEUE_SERIAL); //執行任務 for (int i = 0; i %d",i); dispatch_sync(queue, ^{ NSLog(@"Current Thread=%@---->%d-----",[NSThread currentThread],i); }); } NSLog(@"序列佇列同步end"); } //序列佇列非同步 - (void)serialQueueAsyncMethod{ dispatch_queue_t queue = dispatch_queue_create("serialQueueAsyncMethod", DISPATCH_QUEUE_SERIAL); for (int i = 0; i %d",i); dispatch_async(queue, ^{ NSLog(@"Current Thread=%@---->%d-----",[NSThread currentThread],i); }); } NSLog(@"序列佇列非同步end"); } |
序列佇列 同步執行結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
2016-11-03 17:16:35.794 ThreadDemo[27088:5268309] mainThread--->0 2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread={number = 1, name = main}---->0----- 2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->1 2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread={number = 1, name = main}---->1----- 2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->2 2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread={number = 1, name = main}---->2----- 2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->3 2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread={number = 1, name = main}---->3----- 2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] mainThread--->4 2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread={number = 1, name = main}---->4----- 2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] mainThread--->5 2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread={number = 1, name = main}---->5----- 2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] 序列佇列同步end |
序列佇列 非同步執行結果:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->0 2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->1 2016-11-03 17:22:25.074 ThreadDemo[27122:5273252] Current Thread={number = 5, name = (null)}---->0----- 2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->2 2016-11-03 17:22:25.074 ThreadDemo[27122:5273252] Current Thread={number = 5, name = (null)}---->1----- 2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->3 2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread={number = 5, name = (null)}---->2----- 2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] mainThread--->4 2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread={number = 5, name = (null)}---->3----- 2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] mainThread--->5 2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread={number = 5, name = (null)}---->4----- 2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] 序列佇列非同步end 2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread={number = 5, name = (null)}---->5----- |
小結:
- 從序列佇列同步執行結果看出列印是交替執行的!從列印中看到number=1,說明執行緒block任務是在主執行緒中執行的。因為同步是不會開闢執行緒的,所有當前只有一個主執行緒MainThread。也就是說序列佇列同步執行不會開闢執行緒,所有block任務之間是同步執行的
- 從序列佇列非同步執行結果看出列印並不是交替執行的!從列印中看到number=5,說明執行緒block的任務是在一個全新的執行緒中執行的。因為非同步是會開闢執行緒的,所有當前有主執行緒MainThread和子執行緒number=5。也就是說序列佇列非同步執行會僅會開闢一個新的執行緒,所有block任務之間是同步執行的
- 以先進先出的方式,順序排程佇列中的任務執行
- 無論佇列中指定的任務函式是同步還是非同步,都會等待前一個任務執行完畢以後,再排程後面的任務
2. 併發佇列(Concurrent Dispatch Queue)
併發佇列建立
1 |
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT); |
併發佇列同步和非同步執行Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#pragma mark - 並行佇列同步和並行佇列非同步 //並行佇列同步 - (void)concurrentQueueSyncMethod{ dispatch_queue_t queue = dispatch_queue_create("concurrentQueueSyncMethod", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i %d-----",[NSThread currentThread],i); }); } NSLog(@"並行佇列同步end"); } //並行佇列非同步 - (void)concurrentQueueAsyncMethod{ dispatch_queue_t queue = dispatch_queue_create("concurrentQueueAsyncMethod", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i %d-----",[NSThread currentThread],i); }); } NSLog(@"並行佇列非同步end"); } |
併發 佇列同步執行結果:
1 2 3 4 5 6 7 |
2016-11-03 17:49:33.850 ThreadDemo[27176:5290096] Current Thread={number = 1, name = main}---->0----- 2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread={number = 1, name = main}---->1----- 2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread={number = 1, name = main}---->2----- 2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread={number = 1, name = main}---->3----- 2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread={number = 1, name = main}---->4----- 2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread={number = 1, name = main}---->5----- 2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] 並行佇列同步end |
併發佇列 非同步執行結果:
1 2 3 4 5 6 7 |
2016-11-03 18:33:32.794 ThreadDemo[27283:5311953] 並行佇列非同步end 2016-11-03 18:33:32.794 ThreadDemo[27283:5312009] Current Thread={number = 3, name = (null)}---->0----- 2016-11-03 18:33:32.794 ThreadDemo[27283:5312006] Current Thread={number = 4, name = (null)}---->1----- 2016-11-03 18:33:32.794 ThreadDemo[27283:5312003] Current Thread={number = 6, name = (null)}---->3----- 2016-11-03 18:33:32.794 ThreadDemo[27283:5312174] Current Thread={number = 8, name = (null)}---->5----- 2016-11-03 18:33:32.794 ThreadDemo[27283:5312004] Current Thread={number = 5, name = (null)}---->2----- 2016-11-03 18:33:32.794 ThreadDemo[27283:5312173] Current Thread={number = 7, name = (null)}---->4----- |
小結:
- 併發佇列同步執行和序列佇列同步執行一樣,都不會開闢新執行緒,block任務之間是同步執行的!
- 併發佇列非同步執行結果中看到開闢了多個執行緒,並且執行順序也不是順序執行。因為非同步開多執行緒的代名詞,併發是開多條執行緒的代名詞
- 有多個執行緒,操作進來之後它會將這些佇列安排在可用的處理器上,同時保證先進來的任務優先處理。
- 以先進先出的方式,併發排程佇列中的任務執行
- 如果當前排程的任務是同步執行的,會等待任務執行完成後,再排程後續的任務
- 如果當前排程的任務是非同步執行的,同時底層執行緒池有可用的執行緒資源,會再新的執行緒排程後續任務的執行
3. 全域性佇列(Global Dispatch Queue)
全域性佇列基本知識
dispatch_get_global_queue
函式來獲取- 全域性佇列是所有應用程式都能夠使用的併發佇列(Concurrent Dispatch Queue),沒必要通過
dispatch_queue_create
函式逐個生成併發佇列,只需要獲取Global Dispatch Queue即可 - 是系統為了方便程式設計師開發提供的,其工作表現與併發佇列一致、其工作表現與併發佇列一致、其工作表現與併發佇列一致
- Global Dispatch Queue有4個優先順序,分別是高優先順序、預設優先順序、低優先順序、後臺優先順序!因為XNU核心用於Global Dispatch Queue的執行緒並不能保證實時性,因此執行優先順序知識大概的判斷和區分。
1234#define DISPATCH_QUEUE_PRIORITY_HIGH 2#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0#define DISPATCH_QUEUE_PRIORITY_LOW (-2)#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
全域性佇列同步和非同步執行Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#pragma mark -全域性佇列同步和全域性佇列非同步(工作表現與併發佇列一致) //全域性佇列同步 - (void)globalSyncMethod{ //獲取全域性佇列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //執行任務 for (int i = 0; i %d----",[NSThread currentThread],i); }); } NSLog(@"global_queue_sync_end"); } //全域性佇列非同步 - (void)globalAsyncMethod{ //獲取全域性佇列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //執行任務 for (int i = 0; i %d----",[NSThread currentThread],i); }); } NSLog(@"global_queue_async_end"); } |
全域性佇列 同步執行結果:
1 2 3 4 5 6 7 8 9 10 11 |
2016-11-03 19:06:02.650 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->0---- 2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->1---- 2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->2---- 2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->3---- 2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->4---- 2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->5---- 2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->6---- 2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->7---- 2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->8---- 2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync{number = 1, name = main}---->9---- 2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync_end |
全域性佇列 非同步執行結果:
1 2 3 4 5 6 7 8 9 10 11 |
2016-11-03 19:12:00.242 ThreadDemo[27430:5333537] global_queue_async_end 2016-11-03 19:12:00.242 ThreadDemo[27430:5334057] global_queue_async{number = 5, name = (null)}---->0---- 2016-11-03 19:12:00.242 ThreadDemo[27430:5334053] global_queue_async{number = 6, name = (null)}---->1---- 2016-11-03 19:12:00.242 ThreadDemo[27430:5334056] global_queue_async{number = 7, name = (null)}---->2---- 2016-11-03 19:12:00.242 ThreadDemo[27430:5334063] global_queue_async{number = 8, name = (null)}---->3---- 2016-11-03 19:12:00.242 ThreadDemo[27430:5334064] global_queue_async{number = 9, name = (null)}---->4---- 2016-11-03 19:12:00.243 ThreadDemo[27430:5334057] global_queue_async{number = 5, name = (null)}---->5---- 2016-11-03 19:12:00.243 ThreadDemo[27430:5334053] global_queue_async{number = 6, name = (null)}---->7---- 2016-11-03 19:12:00.243 ThreadDemo[27430:5334065] global_queue_async{number = 10, name = (null)}---->6---- 2016-11-03 19:12:00.243 ThreadDemo[27430:5334056] global_queue_async{number = 7, name = (null)}---->8---- 2016-11-03 19:12:00.243 ThreadDemo[27430:5334066] global_queue_async{number = 11, name = (null)}---->9---- |
4. 主佇列(Main Dispatch Queue)
主佇列基本知識
dispatch_get_main_queue()
函式來獲取- 專門用來在主執行緒上排程任務的佇列
- 不會開啟執行緒
- 以先進先出的方式,在主執行緒空閒時才會排程佇列中的任務在主執行緒執行
- 如果當前主執行緒正在有任務執行,那麼無論主佇列中當前被新增了什麼任務,都不會被排程
主佇列同步和非同步執行Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#pragma mark -主佇列同步和主佇列非同步 //主佇列同步 - (void)mainSyncMethod{ //獲取主佇列 dispatch_queue_t queue = dispatch_get_main_queue(); //執行任務 for (int i = 0; i %d----",[NSThread currentThread],i); }); } NSLog(@"main_queue_sync_end"); } //主佇列非同步 - (void)mainAsyncMethod{ //獲取主佇列 dispatch_queue_t queue = dispatch_get_main_queue(); //執行任務 for (int i = 0; i %d----",[NSThread currentThread],i); }); } NSLog(@"main_queue_async_end"); } |
主佇列 同步執行結果:
1 2 3 4 5 6 7 8 9 |
主執行緒和主佇列相互等待造成死鎖,程式會直接卡死! 原因:原始碼在Main Dispatch Queue 即主佇列中執行指定的block任務,並等待其結束。而其實在主執行緒中正在執行這些原始碼,所以無法執行追加到Main Dispatch Queue 的block任務。 下面例子也一樣: dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"main_queue_sync%@----",[NSThread currentThread]); }); }); |
主佇列 非同步執行結果:
1 2 3 4 5 6 7 8 9 10 11 |
2016-11-03 19:45:38.154 ThreadDemo[27501:5349956] main_queue_async_end 2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->0---- 2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->1---- 2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->2---- 2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->3---- 2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->4---- 2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->5---- 2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->6---- 2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->7---- 2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->8---- 2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async{number = 1, name = main}---->9---- |
5.延遲執行(dispatch_after)
- 在GCD中我們使用dispatch_after()函式來延遲執行佇列中的任務, dispatch_after()是非同步執行佇列中的任務的,也就是說使用dispatch_after()來執行佇列中的任務不會阻塞當前任務。等到延遲時間到了以後就會開闢一個新的執行緒然後執行佇列中的任務。
dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
來建立- 經過我的猜測測試(看不到原始碼),你們也思考一下這個延遲函式的實現過程。底層實現應該是dispath_async函式追加block到Main Dispatch Queue等相應佇列,虛擬碼如下(以Main Dispatch Queue為例):
12345678dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);delay(2){dispatch_async(queue, ^{dispatch_async(dispatch_get_main_queue(), ^{add task to mainQueue});});}
延遲執行(dispatch_after)Demo
1 2 3 4 |
#pragma mark - 延遲執行 - (void)GCDAfterRunMethod{ // 迴圈5次 for (int i =0; i |
注意:
dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
- 第二個引數:指定要追加處理的Dispatch Queue
- 第三個引數:指定要執行處理的Block
- 第一個引數:指定時間用的
dispatch_time_t
型別的值,該值是使用dispatch_time
函式或者dispatch_walltime
函式生成
1.dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
裡面有兩個引數:
123引數1獲取指定的時間開始,通常為DISPATCH_TIME_NOW,定義:#define DISPATCH_TIME_NOW (0ull)//現在#define DISPATCH_TIME_FOREVER (~0ull)//永遠,意味著你用它你的任務永遠不會執行了!
12345678引數2是從引數1延遲指定的時間來執行任務,也是一個時間#define NSEC_PER_SEC 1000000000ull#define NSEC_PER_MSEC 1000000ull#define USEC_PER_SEC 1000000ull#define NSEC_PER_USEC 1000ullull是C語言的數值字面量,是顯示錶明型別是使用的字串(表示“unsigned long long”)NSEC_PER_SEC 是秒的單位數量級 等價於1億納秒上面程式碼中提到的(int64_t)(2 * NSEC_PER_SEC))就是2秒了
2.使用dispatch_walltime
函式生成dispatch_time_t
,用於計算絕對時間,例如在dispatch_after函式中指定2016年11月3日21時45分1秒這一絕對時間,可粗略的鬧鐘功能來使用。也就是到指定時間就會執行任務
12345678910111213141516//由NSDate類物件獲取dispatch_time_t傳遞給dispatch_after函式使用dispatch_time_t getDispatchTimeByDate(NSDate *date){NSTimeInterval interval;double second,subSecond;struct timespec time;dispatch_time_t milestone;interval = [date timeIntervalSince1970];subSecond = modf(interval, &second);time.tv_sec = second;time.tv_nsec = subSecond * NSEC_PER_SEC;milestone = dispatch_walltime(&time, 0);return milestone;}
6. 更改優先順序(dispatch_set_target_queue)
dispatch_queue_create
函式生成的Dispatch Queue不管是 序列佇列(Serial Dispatch Queue) 還是 併發佇列(Concurrent Dispatch Queue) ,都是用與 預設優先順序全域性佇列(Global Dispatch Queue)相同執行優先順序的執行緒。而全域性佇列工作方式與併發佇列工作方式完全一致!dispatch_set_target_queue(dispatch_object_t object,dispatch_queue_t _Nullable queue);
函式來更改佇列優先順序- 第一個引數:指定要變更優先順序的佇列(要更改佇列)
- 第二個引數:指定第一個引數(佇列)要和我們預期佇列執行相同優先順序的佇列(目標佇列)
1. dispatch_set_target_queue第一個Demo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
- (void)GCDSetTargetQueueMethod{ dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT); //更改優先順序 dispatch_set_target_queue(queue1, targetQueue); for (int i = 0; i %d",[NSThread currentThread],i); }); } for (int i = 0; i %d",[NSThread currentThread],i); }); } } |
列印結果:
1 2 3 4 5 6 7 8 9 10 11 12 |
2016-11-04 11:14:57.034 ThreadDemo[28477:5530919] queue2-----currentThread = {number = 11, name = (null)}----->1 2016-11-04 11:14:57.034 ThreadDemo[28477:5530920] queue2-----currentThread = {number = 12, name = (null)}----->2 2016-11-04 11:14:57.034 ThreadDemo[28477:5530918] queue2-----currentThread = {number = 10, name = (null)}----->0 2016-11-04 11:14:57.034 ThreadDemo[28477:5530919] queue2-----currentThread = {number = 11, name = (null)}----->3 2016-11-04 11:14:57.034 ThreadDemo[28477:5530912] queue1-currentThread = {number = 9, name = (null)}-->0 2016-11-04 11:14:57.034 ThreadDemo[28477:5530920] queue2-----currentThread = {number = 12, name = (null)}----->5 2016-11-04 11:14:57.034 ThreadDemo[28477:5530921] queue2-----currentThread = {number = 13, name = (null)}----->4 2016-11-04 11:14:57.035 ThreadDemo[28477:5530912] queue1-currentThread = {number = 9, name = (null)}-->1 2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = {number = 9, name = (null)}-->2 2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = {number = 9, name = (null)}-->3 2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = {number = 9, name = (null)}-->4 2016-11-04 11:14:57.037 ThreadDemo[28477:5530912] queue1-currentThread = {number = 9, name = (null)}-->5 |
列印解釋:
- queue1和queue2設定的目標佇列是全域性佇列(併發),也就是允許要更改的佇列可以開闢多條執行緒(可以超過一條)
- 程式碼中queue1變更和低優先順序的全域性佇列一樣優先順序的序列佇列,queue2是dispatch_queue_create建立和預設優先順序的全域性佇列一樣優先順序的併發佇列。
- queue1和queue2都是非同步執行,都會開闢執行緒,queue2是併發佇列所以列印中看到有多個執行緒,並且任務之間也不是順序執行。queue1是序列佇列,所以只會開闢一個執行緒,任務會順序執行。
- queue2佇列的優先順序比queue1佇列的優先順序要高,從列印中可以看到queue2的確比queue1任務要普遍先執行。為什麼要說普遍呢,因為從結果上看到queue1有一個任務是在queue2裡任務沒有執行完畢也執行了,為什麼會出現這個問題呢??
123在上面全域性佇列中說到了優先順序的事情因為XNU核心用於Global Dispatch Queue的執行緒並不能保證實時性,因此執行優先順序知識大概的判斷和區分,所有我們不能完全依賴這個優先順序來做佇列的順序事情,否則會出現問題!切記
2. dispatch_set_target_queue第一個Demo:
- dispatch_set_target_queue除了能用來設定佇列的優先順序之外,還能夠建立佇列的執行層次
,當我們想讓不同佇列中的任務同步的執行時,我們可以建立一個序列佇列,然後將這些佇列的target指向新建立的佇列即可,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
- (void)GCDSetTargetQueueMethod{ dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);//目標佇列 dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);//序列佇列 dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);//併發佇列 //設定參考 dispatch_set_target_queue(queue1, targetQueue); dispatch_set_target_queue(queue2, targetQueue); for (int i = 0; i %d",[NSThread currentThread],i); }); } for (int i = 0; i %d",[NSThread currentThread],i); }); } } |
列印結果:
1 2 3 4 5 6 7 8 9 10 11 12 |
2016-11-04 11:34:34.722 ThreadDemo[28551:5540844] queue1-currentThread = {number = 3, name = (null)}-->0 2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = {number = 3, name = (null)}-->1 2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = {number = 3, name = (null)}-->2 2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = {number = 3, name = (null)}-->3 2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = {number = 3, name = (null)}-->4 2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = {number = 3, name = (null)}-->5 2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = {number = 3, name = (null)}-->0 2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = {number = 3, name = (null)}-->1 2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = {number = 3, name = (null)}-->2 2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = {number = 3, name = (null)}-->3 2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = {number = 3, name = (null)}-->4 2016-11-04 11:34:34.725 ThreadDemo[28551:5540844] queue2-currentThread = {number = 3, name = (null)}-->5 |
列印解釋:
- queue1和queue2設定的目標佇列是序列佇列,也就是允許要更改的佇列可以開闢一條執行緒。
- queue1和queue2優先順序一樣,他兩執行順序相當於一個序列佇列非同步執行!
- queue1和queue2佇列以targetQueue佇列為參照物件,那麼queue1和queue2中的任務將按照targetQueue的佇列處理。
- 適用場景:一般都是把一個任務放到一個序列的queue中,如果這個任務被拆分了,被放置到多個序列的queue中,但實際還是需要這個任務同步執行,那麼就會有問題,因為多個序列queue之間是並行的。這時候dispatch_set_target_queue將起到作用。
dispatch_set_target_queue小結:
- dispatch_set_target_queue可以更改Dispatch Queue優先順序。
- dispatch_set_target_queue可以更改佇列的執行層次,佇列裡的任務將會按照目標佇列(target Queue)的佇列來處理
7. 任務組Dispatch Group
- GCD的任務組在開發中是經常被使用到,
當你一組任務結束後再執行一些操作時,使用任務組在合適不過了
。dispatch_group的職責就是當佇列中的所有任務都執行完畢後在去做一些操作,也就是說在任務組中執行的佇列,當佇列中的所有任務都執行完畢後就會發出一個通知來告訴使用者任務組中所執行的佇列中的任務執行完畢了。關於將佇列放到任務組中執行有兩種方式,一種是使用dispatch_group_async()
函式,將佇列與任務組進行關聯並自動執行佇列中的任務。另一種方式是手動的將佇列與組進行關聯然後使用非同步將佇列進行執行,也就是dispatch_group_enter()
與dispatch_group_leave()
方法的使用。下方就給出詳細的介紹。
1.佇列與組自動關聯並執行
- 首先我們來介紹
dispatch_group_async()
函式的使用方式,該函式會將佇列與相應的任務組進行關聯,並且自動執行。當與任務組關聯的佇列中的任務都執行完畢後,會通過dispatch_group_notify()
函式發出通知告訴使用者任務組中的所有任務都執行完畢了。使用通知的方式是不會阻塞當前執行緒的
,如果你使用dispatch_group_wait()函式,那麼就會阻塞當前執行緒,直到任務組中的所有任務都執行完畢。
12345678910111213//自動執行任務組- (void)GCDAutoDispatchGroupMethod{dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group = dispatch_group_create();for (int i = 0; i %d",[NSThread currentThread],i);});}dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"current Thread = %@----->這是最後執行",[NSThread currentThread]);});}列印結果:
12345672016-11-04 15:11:34.010 ThreadDemo[29225:5641180] current Thread = {number = 5, name = (null)}----->22016-11-04 15:11:34.010 ThreadDemo[29225:5641193] current Thread = {number = 3, name = (null)}----->02016-11-04 15:11:34.011 ThreadDemo[29225:5641438] current Thread = {number = 8, name = (null)}----->52016-11-04 15:11:34.010 ThreadDemo[29225:5641178] current Thread = {number = 6, name = (null)}----->32016-11-04 15:11:34.010 ThreadDemo[29225:5641177] current Thread = {number = 4, name = (null)}----->12016-11-04 15:11:34.010 ThreadDemo[29225:5641437] current Thread = {number = 7, name = (null)}----->42016-11-04 15:11:34.011 ThreadDemo[29225:5641137] current Thread = {number = 1, name = main}----->這是最後執行 - 上面的函式就是使用
dispatch_group_async()
函式將佇列與任務組進行關聯並執行。首先我們建立了一個全域性佇列(併發),然後又建立了一個型別為dispatch_group_t
的任務組group。使用dispatch_group_async()
函式將兩者進行關聯並執行。使用dispatch_group_notify()
函式進行監聽group中佇列的執行結果,如果執行完畢後,我們就在主執行緒中對結果進行處理。
dispatch_group_notify()
函式有兩個引數一個是傳送通知的group,另一個是處理返回結果的佇列。dispatch_group_async(dispatch_group_t group,dispatch_queue_t queue,dispatch_block_t block);
函式與dispatch_async
函式相同,都追加block到指定的Dispatch Queue中,與dispatch_async不同的是指定生成的Dispatch Group為第一個引數。指定的block屬於指定的Dispatch Group,不是Dispatch Queue,切記!
- 無論向什麼樣的Dispatch Queue中追加處理,使用Dispatch Group都可監視這些處理執行的結束。一旦檢測到所有處理執行結束,就可將結束的處理追加到Dispatch Queue中。這就是使用Dispatch Group的原因。
- 在追加到Dispatch Group中的處理全部執行結束時,程式碼中使用的
dispatch_group_notify
函式會將執行的Block追加到Dispatch Queue中,將第一個引數指定為要監視的Dispatch Group。在追加到該Dispatch Group的全部處理執行結束時,將第三個引數的Block追加到第二個引數的Dispatch Queue中。在dispatch_group_notify函式中不管指定什麼樣的Dispatch Queue,屬於Dispatch Group的全部處理在追加指定的Block時都已執行結束。 - 另外,在Dispatch Group中也可以使用
dispatch_group_wait
函式僅等待全部處理執行結束。
1 2 3 4 5 6 7 8 |
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{NSLog(@"blk0");}); dispatch_group_async(group, queue, ^{NSLog(@"blk1");}); dispatch_group_async(group, queue, ^{NSLog(@"blk2");}); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); |
dispatch_group_wait
函式的第二個引數指定為等待時間(超時)。它屬於dispatch_time_t型別的值。程式碼中使用的DISPATCH_TIME_FOREVER
,意味著永久等待。只要屬於Dispatch Group的操作尚未執行結束,就會一直等待,中途不能取消。指定等待時間為1微秒時,應做如下處理:
123456789101112//等待group處理結束// dispatch_group_wait(group, DISPATCH_TIME_FOREVER);dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1*USEC_PER_SEC));//1微秒long result = dispatch_group_wait(group, time);if (result == 0) {//屬於Dispatch Group 的block任務全部處理結束NSLog(@"Dispatch Group全部處理完畢");}else{//屬於Dispatch Group 的block任務還在處理中NSLog(@"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的處理全部執行結束之前,執行該函式的執行緒停止,屬於阻塞狀態。
- 指定
DISPACH_TIME_NOW
,則不用任何等待即可判定屬於Dispatch Group的處理是否執行結束。1long result = diaptach_group_wait(group, DISPACH_TIME_NOW); - 在主執行緒的RunLoop的每次迴圈中,可檢查執行是否結束,從而不消耗多餘的等待時間,雖然這樣有額可以,但一般在這種情況下,還是推薦用
dispatch_group_notify
函式追加結束處理到Main Dispatch Queue中。這是因為dispatch_group_notify
函式可以簡化程式碼。並且如果你用了diaptach_group_wait
等待時間過長,中間不能取消佇列任務這就很坑了!
2. 佇列與組手動關聯並執行
- 接下來我們將手動的管理任務組與佇列中的關係,也就是不使用
dispatch_group_async()
函式。我們使用dispatch_group_enter()
與dispatch_group_leave()
函式將佇列中的每次任務加入到到任務組中。首先我們使用dispatch_group_enter()
函式進入到任務組中,然後非同步執行佇列中的任務,最後使用dispatch_group_leave()
函式離開任務組即可。下面的函式中我們使用了dispatch_group_wait()
函式,該函式的職責就是阻塞當前執行緒,來等待任務組中的任務執行完畢。該函式的第一個引數是所要等待的group,第二個引數是等待超時時間,此處我們設定的是DISPATCH_TIME_FOREVER
,就說明等待任務組的執行永不超時,直到任務組中所有任務執行完畢。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//手動執行任務組 - (void)GCDManualDispatchGroupMethod{ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); for (int i = 0; i %d",[NSThread currentThread],i); dispatch_group_leave(group);//離開佇列組 }); } long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//阻塞當前執行緒,直到所有任務執行完畢才會繼續往下執行 if (result == 0) { //屬於Dispatch Group 的block任務全部處理結束 NSLog(@"Dispatch Group全部處理完畢"); }else{ //屬於Dispatch Group 的block任務還在處理中 NSLog(@"Dispatch Group正在處理"); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"current Thread = %@----->這是最後執行",[NSThread currentThread]); }); } |
列印結果:dispatch_group_wait()
函式下方的print()函式在所有任務執行完畢之前是不會被呼叫的,因為dispatch_group_wait()
會將當前執行緒進行阻塞。當然雖然是手動的將佇列與任務組進行關聯的,感覺display_group_notify()
函式還是好用的。
1 2 3 4 5 6 7 8 |
2016-11-04 16:19:13.802 ThreadDemo[29402:5678525] current Thread = {number = 5, name = (null)}----->2 2016-11-04 16:19:13.802 ThreadDemo[29402:5678527] current Thread = {number = 3, name = (null)}----->0 2016-11-04 16:19:13.802 ThreadDemo[29402:5678524] current Thread = {number = 4, name = (null)}----->1 2016-11-04 16:19:13.803 ThreadDemo[29402:5678760] current Thread = {number = 8, name = (null)}----->5 2016-11-04 16:19:13.802 ThreadDemo[29402:5678545] current Thread = {number = 6, name = (null)}----->3 2016-11-04 16:19:13.802 ThreadDemo[29402:5678544] current Thread = {number = 7, name = (null)}----->4 2016-11-04 16:19:13.803 ThreadDemo[29402:5678489] Dispatch Group全部處理完畢 2016-11-04 16:19:13.804 ThreadDemo[29402:5678489] current Thread = {number = 1, name = main}----->這是最後執行 |
8. 柵欄任務Dispatch_barrier_async
- barrier顧名思義柵欄、障礙物的意思!
在訪問資料庫或檔案時,使用Serial Dispatch Queue可避免資料競爭的問題。寫入處理確實不可與其他的寫入處理以及包含讀取處理的其他某些處理並行執行。但是如果讀取處理只是與讀取處理並行執行,那麼多個並行執行就不會發生問題。
也就是說,為了高效率地進行訪問,讀取處理追加到Concurrent Dispatch Queue中,寫入出路在任何一個讀取處理沒有執行的狀態下,追加到Serial Dispatch Queue中即可(在寫入處理結束之前,讀取處理不可執行)。
雖然利用Dispatch Group和dispatch_set_target_queue函式也可實現,但程式碼會很複雜。有興趣的可以自己嘗試寫寫!
CD為我們提供了更為聰明的解決辦法——dispatch_barrier_async
函式。該函式同dispatch_queue_create函式生成的Concurrent Dispatch Queue一起使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#pragma mark - Dispatch_barrier_async - (void)GCDBarrierAsyncMethod{ dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT); void(^blk1_reading)(void) = ^{ NSLog(@"blk1---reading"); }; void(^blk2_reading)(void) = ^{ NSLog(@"blk2---reading"); }; void(^blk3_reading)(void) = ^{ NSLog(@"blk3---reading"); }; void(^blk4_reading)(void) = ^{ NSLog(@"blk4---reading"); }; void(^blk_writing)(void) = ^{ NSLog(@"blk---writing"); }; dispatch_async(concurrentQueue, blk1_reading); dispatch_async(concurrentQueue, blk2_reading); //新增追加操作,,會等待b1和b2全部執行結束,執行完成追加操作b,才會繼續併發執行下面操作 dispatch_barrier_async(concurrentQueue, blk_writing); dispatch_async(concurrentQueue, blk3_reading); dispatch_async(concurrentQueue, blk4_reading); } |
列印結果:
1 2 3 4 5 |
2016-11-04 17:02:13.202 ThreadDemo[29492:5700974] blk2---reading 2016-11-04 17:02:13.202 ThreadDemo[29492:5700972] blk1---reading 2016-11-04 17:02:13.203 ThreadDemo[29492:5700972] blk---writing 2016-11-04 17:02:13.203 ThreadDemo[29492:5700972] blk3---reading 2016-11-04 17:02:13.203 ThreadDemo[29492:5700974] blk4---reading |
- 使用
Concurrent Dispatch Queue
和dispatch_barrier_async
函式可實現高效率的資料庫訪問和檔案訪問,dispatch_barrier_async
函式還是比較好理解的。
9. 迴圈執行dispatch_apply
- dispatch_apply()函式是用來迴圈來執行佇列中的任務的,使用方式為:
dispatch_apply(迴圈次數, 任務所在的佇列) { 要迴圈執行的任務 }
。使用該函式迴圈執行並行佇列中的任務時,會開闢新的執行緒,不過有可能會在當前執行緒中執行一些任務。而使用dispatch_apply()執行序列佇列中的任務時,會在當前執行緒中執行。無論是使用並行佇列還是序列佇列,dispatch_apply()都會阻塞當前執行函式執行緒。
1 2 3 4 5 6 |
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(10, queue, ^(size_t index){ NSLog(@"%zu", index); }); NSLog(@"done"); |
1 2 3 4 5 6 7 8 9 10 11 |
2016-11-04 17:24:25.819 ThreadDemo[29598:5715955] 2 2016-11-04 17:24:25.819 ThreadDemo[29598:5715904] 0 2016-11-04 17:24:25.819 ThreadDemo[29598:5716588] 1 2016-11-04 17:24:25.819 ThreadDemo[29598:5716640] 3 2016-11-04 17:24:25.820 ThreadDemo[29598:5715955] 4 2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] 5 2016-11-04 17:24:25.820 ThreadDemo[29598:5716588] 6 2016-11-04 17:24:25.820 ThreadDemo[29598:5716640] 7 2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] 9 2016-11-04 17:24:25.820 ThreadDemo[29598:5715955] 8 2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] done |
- 輸出結果中最後的done必定在最後的位置上,這是因為dispatch_apply函式會等待全部處理執行結束,也就是阻塞當前執行函式執行緒。
- 另外,由於
dispatch_apply
函式也與dispatch_sync
函式相同,會等待處理執行結束(阻塞),因此推薦在dispatch_async
函式中非同步地執行dispatch_apply
函式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
#pragma mark - Dispatch_apply - (void)GCDDispatchApplyMethod{ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); void(^blk1_reading)(void) = ^{ NSLog(@"blk1---reading"); }; void(^blk2_reading)(void) = ^{ NSLog(@"blk2---reading"); }; void(^blk3_reading)(void) = ^{ NSLog(@"blk3---reading"); }; void(^blk_writing)(void) = ^{ NSLog(@"blk---writing"); }; NSMutableArray *array = [NSMutableArray new]; [array addObject:blk1_reading]; [array addObject:blk2_reading]; [array addObject:blk3_reading]; [array addObject:blk_writing]; dispatch_async(queue, ^{ dispatch_apply(array.count, queue, ^(size_t index) { void (^blk)(void) = [array objectAtIndex:index]; blk(); NSLog(@"%zu====%@",index,[array objectAtIndex:index]); }); NSLog(@"全部執行結束"); dispatch_async(dispatch_get_main_queue(), ^{ //在main Dispatch queue中執行處理,更新使用者介面等待 NSLog(@"done"); }); }); } |
列印結果:
1 2 3 4 5 6 7 8 9 10 |
2016-11-04 17:36:02.258 ThreadDemo[29635:5723967] blk3---reading 2016-11-04 17:36:02.258 ThreadDemo[29635:5723940] blk2---reading 2016-11-04 17:36:02.258 ThreadDemo[29635:5723929] blk1---reading 2016-11-04 17:36:02.258 ThreadDemo[29635:5723968] blk---writing 2016-11-04 17:36:02.259 ThreadDemo[29635:5723940] 1==== 2016-11-04 17:36:02.259 ThreadDemo[29635:5723967] 2==== 2016-11-04 17:36:02.259 ThreadDemo[29635:5723929] 0==== 2016-11-04 17:36:02.259 ThreadDemo[29635:5723968] 3==== 2016-11-04 17:36:02.259 ThreadDemo[29635:5723940] 全部執行結束 2016-11-04 17:36:02.259 ThreadDemo[29635:5723499] done |
10. 佇列的掛起和喚醒
- 佇列的掛起與喚醒相對較為簡單,如果你想對一個佇列中的任務的執行進行掛起,那麼你就使用
dispatch_suspend()
函式即可。如果你要喚醒某個掛起的佇列,那麼你就可以使用dispatch_resum()
函式。這兩個函式所需的引數都是你要掛起或者喚醒的佇列,鑑於知識點的簡單性就不做過多的贅述了。
1 2 3 4 5 6 7 8 9 10 11 |
#pragma mark -Dispatch_suspend/Dispatch_resume - (void)GCDDispatch_suspend_resume{ //系統預設生成的,所以無法呼叫dispatch_resume()和dispatch_suspend()來控制執行繼續或中斷。 dispatch_queue_t queue1 = dispatch_queue_create("queue1", 0); dispatch_queue_t queue2 = dispatch_queue_create("queue2", 0); dispatch_group_t group = dispatch_group_create(); dispatch_async(queue1, ^{ for (int i = 0; i |
列印結果:可以先思考一下列印結果
1 2 3 4 5 6 7 8 9 10 |
2016-11-04 17:59:41.219 ThreadDemo[29749:5738141] task2 2016-11-04 17:59:41.219 ThreadDemo[29749:5738138] {number = 4, name = (null)}-------0 2016-11-04 17:59:41.220 ThreadDemo[29749:5738141] task2 finished!掛起queue1 2016-11-04 17:59:42.220 ThreadDemo[29749:5738138] {number = 4, name = (null)}-------1 2016-11-04 17:59:43.223 ThreadDemo[29749:5738138] {number = 4, name = (null)}-------2 2016-11-04 17:59:44.225 ThreadDemo[29749:5738138] {number = 4, name = (null)}-------3 2016-11-04 17:59:45.230 ThreadDemo[29749:5738138] {number = 4, name = (null)}-------4 2016-11-04 18:00:01.220 ThreadDemo[29749:5738141] task1 finished! 2016-11-04 18:00:01.220 ThreadDemo[29749:5738141] task3 2016-11-04 18:00:01.221 ThreadDemo[29749:5738769] task4 |
GCD總結
- 開不開執行緒由執行任務的函式決定
- 非同步開,非同步是多執行緒的代名詞
- 同步不開
- 開幾條執行緒由佇列決定
- 序列佇列開一條執行緒(GCD會開一條,NSOperation Queue最大併發數為1時也可能開多條)
- 併發佇列開多條執行緒,具體能開的執行緒數量由底層執行緒池決定
- GCD的使用步驟
- 1.建立block任務
- 2.建立GCD佇列(序列和併發)
- 3.把block任務放到GCD佇列裡,以非同步或者同步方式執行GCD佇列
結尾:
能看到這裡的小夥伴都是真愛啊,內容太多了。學完這個你們多思考一下里面方法的聯絡以及實現,好了今天的GCD就講到這裡,中間有什麼錯誤歡迎你們批評指出!下一篇部落格將帶你走進NSOperation。
喜歡的話就請點個喜歡加個關注^_^(樓主好無恥啊)❤️
參考書籍:Objective-C高階程式設計iOS與OS X多執行緒和記憶體管理