iOS 開發多線程 —— GCD,ios開發多線程gcd
本文是根據文頂頂老師的部落格學習總結而來,如有不妥之處,還望指出。http://www.cnblogs.com/wendingding/p/3807716.html
一. 任務和隊列
GCD中有2個核心概念
(1)任務:執行什麼操作
(2)隊列:用來存放任務
GCD的使用就2個步驟
(1)定製任務
(2)確定想做的事情
將任務添加到隊列中,GCD會自動將隊列中的任務取出,放到對應的線程中執行
提示:任務的取出遵循隊列的FIFO原則:先進先出,後進後出
二. 執行任務
1. GCD中有2個用來執行任務的函數
說明:把右邊的參數(任務)提交給左邊的參數(隊列)進行執行。
(1)用同步的方式執行任務 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
參數說明:
queue:隊列
block:任務
(2)用非同步方式執行任務 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
2. 同步和非同步區別
同步:在當前線程中執行
非同步:在另一條線程中執行
三. 隊列
1. 隊列的類型
GCD的隊列可以分為2大類型
(1)並發隊列(Concurrent Dispatch Queue)
可以讓多個任務並發(同時)執行(自動開啟多個線程同時執行任務)並發功能只有在非同步(dispatch_async)函數下才有效
(2)串列隊列(Serial Dispatch Queue)
讓任務一個接著一個地執行(一個任務執行完畢後,再執行下一個任務)
2. 補充說明
有4個術語比較容易混淆:同步、非同步、並發、串列
同步和非同步決定了要不要開啟新的線程
同步:在當前線程中執行任務,不具備開啟新線程的能力
非同步:在新的線程中執行任務,具備開啟新線程的能力
並發和串列決定了任務的執行方式
並發:多個任務並發(同時)執行
串列:一個任務執行完畢後,再執行下一個任務
3. 串列隊列
GCD中獲得串列有2種途徑
(1)使用dispatch_queue_create函數建立串列隊列
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); // 隊列名稱, 隊列屬性,一般用NULL即可
樣本:
dispatch_queue_t queue = dispatch_queue_create("wendingding", NULL); // 建立
dispatch_release(queue); // 非ARC需要釋放手動建立的隊列
(2)使用主隊列(跟主線程相關聯的隊列)
主隊列是GCD內建的一種特殊的串列隊列,放在主隊列中的任務,都會放到主線程中執行
使用dispatch_get_main_queue()獲得主隊列
樣本:
dispatch_queue_t queue = dispatch_get_main_queue();
4. 並發隊列
GCD預設已經提供了全域的並發隊列,供整個應用使用,不需要手動建立
使用dispatch_get_global_queue函數獲得全域的並發隊列
dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); // 此參數暫時無用,用0即可
樣本:
這個參數是留給以後用的,暫時用不上,傳個0。
第一個參數為優先順序,這裡選擇預設的。擷取一個全域的預設優先順序的並發隊列。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 獲得全域並發隊列
常見的建立方式:
//用同步函數向串列隊列新增工作,不開闢新線程 dispatch_sync(dispatch_queue_create("syncqqq", NULL), ^{ NSLog(@"currentThread=%@",[NSThread currentThread]); }); //currentThread=<NSThread: 0x17006bb00>{number = 1, name = main} //用同步函數向並發隊列新增工作,不開闢新線程 dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"currentThread=%@",[NSThread currentThread]); }); //currentThread=<NSThread: 0x17006bb00>{number = 1, name = main} //用非同步函數向串列隊列新增工作,開闢新線程(只開闢一條線程,任務串列執行) dispatch_queue_t queue = dispatch_queue_create("www", NULL); dispatch_async(queue, ^{ for (int i=0; i<10000; i++) { NSLog(@"currentThread1=%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ NSLog(@"currentThread2=%@",[NSThread currentThread]); }); //currentThread1=<NSThread: 0x17007c500>{number = 4, name = (null)} //currentThread2=<NSThread: 0x17007c500>{number = 4, name = (null)} //用非同步函數向並發隊列新增工作,開闢新線程(有多少任務開闢多少線程,任務並發執行) dispatch_queue_t aqueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(aqueue, ^{ for (int i=0; i<10000; i++) { NSLog(@"currentThread3=%@",[NSThread currentThread]); } }); dispatch_async(aqueue, ^{ NSLog(@"currentThread4=%@",[NSThread currentThread]); });
說明:同步函數不具備開啟線程的能力,無論是什麼隊列都不會開啟線程;非同步函數具備開啟線程的能力,開啟幾條線程由隊列決定(串列隊列只會開啟一條新的線程,並發隊列會開啟多條線程)。
同步函數
(1)並發隊列:不會開線程
(2)串列隊列:不會開線程
非同步函數
(1)並發隊列:能開啟N條線程
(2)串列隊列:開啟1條線程
常見用法:
1.從子線程回到主線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ //在這執行一些耗時操作 dispatch_async(dispatch_get_main_queue(), ^{ //回到主線程進行操作 }); });
注意:
dispatch_sync(dispatch_get_main_queue(), ^{
});
這種寫法是錯誤的,會造成程式崩潰.
2.延遲調用
(1)
[self performSelector:@selector(run) withObject:nil afterDelay:5.0];
注意:如果將
[self performSelector:@selector(run) withObject:nil afterDelay:5.0];放到子線程中,例如:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelector:@selector(run) withObject:nil afterDelay:5.0];
});
這時候程式不會運行 run 方法。
正確寫法:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self performSelector:@selector(run) withObject:nil afterDelay:5.0];
[[NSRunLoop currentRunLoop] run];
});
(2)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//5秒後執行這裡的操作,可以選擇在哪個線程中執行,即:可以用dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 代替 dispatch_get_main_queue()
});