參考: GCD原始碼 深入理解 GCD iOS多執行緒--徹底學會多執行緒之『GCD』 關於iOS多執行緒,我說,你聽,沒準你就懂了
####任務執行方式
- 同步執行(dispatch_sync):只能在當前執行緒中執行任務,不具備開啟新執行緒的能力。必須等到Block函式執行完畢後,dispatch函式才會返回。
- 非同步執行(dispatch_async):可以在新的執行緒中執行任務,具備開啟新執行緒的能力。dispatch函式會立即返回, 然後Block在後臺非同步執行。
####任務管理方式
- 序列佇列:所有任務會在一條執行緒中執行(有可能是當前執行緒也有可能是新開闢的執行緒),並且一個任務執行完畢後,才開始執行下一個任務。(等待完成)
- 並行佇列:可以開啟多條執行緒並行執行任務(但不一定會開啟新的執行緒),並且當一個任務放到指定執行緒開始執行時,下一個任務就可以開始執行了。(等待發生)
// 主佇列--序列,所有放在主佇列中的任務,都會放到主執行緒中執行
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 全域性佇列--並行,系統為我們建立好的一個並行佇列,使用起來與我們自己建立的並行佇列無本質差別
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
// new序列佇列
dispatch_queue_t queue1 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// new並行佇列
dispatch_queue_t queue2 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
複製程式碼
注意:避免使用 GCD Global佇列建立Runloop常駐執行緒 全域性佇列的底層是一個執行緒池,向全域性佇列中提交的 block,都會被放到這個執行緒池中執行,如果執行緒池已滿,後續再提交 block 就不會再重新建立執行緒。等待有空閒的執行緒在執行任務。 所以: 避免使用 GCD Global 佇列建立 Runloop 常駐執行緒,如果n條執行緒都被霸佔了,Global佇列就費了。
####任務+佇列
序列佇列 | 並行佇列 | 主佇列 | |
---|---|---|---|
同步(sync) | 當前執行緒,序列執行 | 佇列當前執行緒,序列執行 | 主新執行緒,序列執行(注意死鎖) |
非同步(async) | 開1條新執行緒,序列執行 | 開n條新執行緒,非同步執行(n在iphone7上面最大是幾十個) | 主新執行緒,序列執行 |
####Dispatch Block
佇列執行任務都是block的方式,
######建立block
- (void)createDispatchBlock {
// 一般的block
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"run block");
});
dispatch_async(concurrentQueue, block);
//QOS優先順序的block
dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
NSLog(@"run qos block");
});
dispatch_async(concurrentQueue, qosBlock);
}
複製程式碼
dispatch_block_wait:可以根據dispatch block來設定等待時間,引數DISPATCH_TIME_FOREVER會一直等待block結束
- (void)dispatchBlockWaitDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"star");
[NSThread sleepForTimeInterval:5.f];
NSLog(@"end");
});
dispatch_async(serialQueue, block);
//設定DISPATCH_TIME_FOREVER會一直等到前面任務都完成
dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
NSLog(@"ok, now can go on");
}
複製程式碼
dispatch_block_notify:可以監視指定dispatch block結束,然後再加入一個block到佇列中。三個引數分別為,第一個是需要監視的block,第二個引數是需要提交執行的佇列,第三個是待加入到佇列中的block
- (void)dispatchBlockNotifyDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t firstBlock = dispatch_block_create(0, ^{
NSLog(@"first block start");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"first block end");
});
dispatch_async(serialQueue, firstBlock);
dispatch_block_t secondBlock = dispatch_block_create(0, ^{
NSLog(@"second block run");
});
//first block執行完才在serial queue中執行second block
dispatch_block_notify(firstBlock, serialQueue, secondBlock);
}
複製程式碼
dispatch_block_cancel:iOS8之後可以呼叫dispatch_block_cancel來取消(需要注意必須用dispatch_block_create建立dispatch_block_t) 需要注意的是,未執行的可以用此方法cancel掉,若已經執行則cancel不了 如果想中斷(interrupt)執行緒,可以使用dispatch_block_testcancel方法
- (void)dispatchBlockCancelDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t firstBlock = dispatch_block_create(0, ^{
NSLog(@"first block start");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"first block end");
});
dispatch_block_t secondBlock = dispatch_block_create(0, ^{
NSLog(@"second block run");
});
dispatch_async(serialQueue, firstBlock);
dispatch_async(serialQueue, secondBlock);
//取消secondBlock
dispatch_block_cancel(secondBlock);
}
複製程式碼
#####1. 序列佇列 + 同步執行 不會開啟新執行緒,在當前執行緒執行任務。任務是序列的,執行完一個任務,再執行下一個任務
- (void)serialQueueSync{
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t serialQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x60800007f840>{number = 3, name = (null)}
1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
4========<NSThread: 0x60800007f840>{number = 3, name = (null)}
*/
}
複製程式碼
#####2. 序列佇列 + 非同步執行 開一個新執行緒,一個一個執行任務
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x6000002616c0>{number = 1, name = main}
4========<NSThread: 0x6000002616c0>{number = 1, name = main}
1========<NSThread: 0x608000270540>{number = 3, name = (null)}
2========<NSThread: 0x608000270540>{number = 3, name = (null)}
3========<NSThread: 0x608000270540>{number = 3, name = (null)}
*/
複製程式碼
#####3. 並行佇列 + 同步執行 當前執行緒,一個一個執行任務
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x60800007f840>{number = 3, name = (null)}
1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
4========<NSThread: 0x60800007f840>{number = 3, name = (null)}
*/
複製程式碼
#####4. 並行佇列 + 非同步執行
開多個執行緒,非同步執行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x608000070300>{number = 1, name = main}
4========<NSThread: 0x608000070300>{number = 1, name = main}
2========<NSThread: 0x608000264140>{number = 4, name = (null)}
1========<NSThread: 0x60000007a800>{number = 3, name = (null)}
3========<NSThread: 0x6080002642c0>{number = 5, name = (null)}
*/
複製程式碼
#####5. 主佇列 + 非同步執行
主執行緒,同步執行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x60000026e000>{number = 3, name = (null)}
4========<NSThread: 0x60000026e000>{number = 3, name = (null)}
1========<NSThread: 0x60000007e2c0>{number = 1, name = main}
2========<NSThread: 0x60000007e2c0>{number = 1, name = main}
3========<NSThread: 0x60000007e2c0>{number = 1, name = main}
*/
複製程式碼
#####6. 主佇列 + 同步執行 (不能在主佇列這麼用,死鎖) 主執行緒,同步執行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x600000263840>{number = 3, name = (null)}
1========<NSThread: 0x608000078ec0>{number = 1, name = main}
2========<NSThread: 0x608000078ec0>{number = 1, name = main}
3========<NSThread: 0x608000078ec0>{number = 1, name = main}
4========<NSThread: 0x600000263840>{number = 3, name = (null)}
*/
複製程式碼
####GCD其他用法 #####dispatch_after延時 1、time = 0,是直接呼叫非同步dispatch_async 2、time > 0, 只是延時提交block,不是延時立刻執行。
//2秒延時、在主佇列
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
});
複製程式碼
#####dispatch_once與dispatch_once_t
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//
});
複製程式碼
1、dispatch_once並不是簡單的只執行一次那麼簡單 2、dispatch_once本質上可以接受多次請求,會對此維護一個請求連結串列 3、如果在block執行期間,多次進入呼叫同類的dispatch_once函式(即單例函式),會導致整體連結串列無限增長,造成永久性死鎖。
遞迴互相巢狀,如下:
- (void)viewDidLoad {
[super viewDidLoad];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 連結串列無限增長
[self viewDidLoad];
});
}
複製程式碼
dispatch_once原始碼
static void
dispatch_once_f_slow(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
#if DISPATCH_GATE_USE_FOR_DISPATCH_ONCE
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
if (_dispatch_once_gate_tryenter(l)) {
_dispatch_client_callout(ctxt, func);
_dispatch_once_gate_broadcast(l);
} else {
_dispatch_once_gate_wait(l);
}
#else
_dispatch_once_waiter_t volatile *vval = (_dispatch_once_waiter_t*)val;
struct _dispatch_once_waiter_s dow = { };
_dispatch_once_waiter_t tail = &dow, next, tmp;
dispatch_thread_event_t event;
if (os_atomic_cmpxchg(vval, NULL, tail, acquire)) {
// 第一次dispatch_once,原子性操作
// 當前執行緒
dow.dow_thread = _dispatch_tid_self();
// 執行block
_dispatch_client_callout(ctxt, func);
// 第一次執行完了,設定token = DISPATCH_ONCE_DONE
next = (_dispatch_once_waiter_t)_dispatch_once_xchg_done(val);
while (next != tail) {
// 繼續去下一個
tmp = (_dispatch_once_waiter_t)_dispatch_wait_until(next->dow_next);
event = &next->dow_event;
next = tmp;
// 訊號量++
_dispatch_thread_event_signal(event);
}
} else {
// 第二次dispatch_once進來
_dispatch_thread_event_init(&dow.dow_event);
next = *vval;
for (;;) {
if (next == DISPATCH_ONCE_DONE) { // token是否等於DISPATCH_ONCE_DONE
// 第一次執行完之後,都是走這裡
break;
}
// 如果是巢狀使用,第一次沒有完成,又要執行一次
if (os_atomic_cmpxchgv(vval, next, tail, &next, release)) {
// 原子性
dow.dow_thread = next->dow_thread;
dow.dow_next = next;
if (dow.dow_thread) {
pthread_priority_t pp = _dispatch_get_priority();
_dispatch_thread_override_start(dow.dow_thread, pp, val);
}
// 等待訊號量
_dispatch_thread_event_wait(&dow.dow_event);
if (dow.dow_thread) {
_dispatch_thread_override_end(dow.dow_thread, val);
}
break;
}
}
_dispatch_thread_event_destroy(&dow.dow_event);
}
#endif
}
複製程式碼
#####dispatch_apply(count,queue,block(index))迭代方法 該函式按指定的次數將指定的block追加到指定的佇列;使用的地方,阻塞當前執行緒
NSLog(@"CurrentThread------%@", [NSThread currentThread]);
//dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 6是次數
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd------%@",index, [NSThread currentThread]);
});
/*
併發佇列:開多執行緒非同步執行
NSLogx資訊
CurrentThread------<NSThread: 0x600000268a40>{number = 3, name = (null)}
0------<NSThread: 0x600000268a40>{number = 3, name = (null)}
1------<NSThread: 0x608000266e40>{number = 4, name = (null)}
2------<NSThread: 0x608000266f00>{number = 5, name = (null)}
3------<NSThread: 0x608000266f40>{number = 6, name = (null)}
4------<NSThread: 0x600000268a40>{number = 3, name = (null)}
5------<NSThread: 0x608000266e40>{number = 4, name = (null)}
*/
/*
同步佇列:當前執行緒同步執行
NSLogx資訊
CurrentThread------<NSThread: 0x608000072c00>{number = 3, name = (null)}
0------<NSThread: 0x6000000694c0>{number = 1, name = main}
1------<NSThread: 0x6000000694c0>{number = 1, name = main}
2------<NSThread: 0x6000000694c0>{number = 1, name = main}
3------<NSThread: 0x6000000694c0>{number = 1, name = main}
4------<NSThread: 0x6000000694c0>{number = 1, name = main}
5------<NSThread: 0x6000000694c0>{number = 1, name = main}
*/
複製程式碼
dispatch_apply能避免執行緒爆炸,因為GCD會管理併發
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 999; i++){
dispatch_async(queue, ^{
NSLog(@"%d,%@",i,[NSThread currentThread]);// 能開多大執行緒就開多大執行緒(幾十個)
});
}
複製程式碼
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(999, queue, ^(size_t i){
NSLog(@"%d,%@",i,[NSThread currentThread]); // 只開一定數量的執行緒(幾個)
});
複製程式碼
#####dispatch_suspend、dispatch_resume (用在dispatch_get_global_queue主佇列無效)
dispatch_suspend,dispatch_resume提供了“掛起、恢復”佇列的功能,簡單來說,就是可以暫停、恢復佇列上的任務。但是這裡的“掛起”,並不能保證可以立即停止佇列上正在執行的block
注意點: ######1、如果佇列沒有使用dispatch_suspend,使用dispatch_resume會crash
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_resume(queue); // crash
}
複製程式碼
######2、如果queue被掛起,queue銷燬時候沒有被喚醒,會crash
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_suspend(queue);// 如果queue被掛起,queue銷燬時候沒有被喚醒,會crash
複製程式碼
######3、dispatch_suspend後面執行dispatch_sync,阻塞當前執行緒,需要其他執行緒恢復佇列
queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_suspend(queue);
// 後面執行dispatch_sync,阻塞當前執行緒,需要其他執行緒恢復佇列
dispatch_sync(queue, ^{
NSLog(@"=====%@",@"111111");
});
NSLog(@"=====%@",@"22222");
複製程式碼
####GCD的佇列組dispatch_group_t,其實就是封裝了一個無限大的訊號量, 注意事項 1、dispatch_group_async(只有async,無sync)等價於{dispatch_group_enter() + async}, async呼叫完了會執行dispatch_group_leave()。 2、dispatch_group_enter()就是訊號量--; 3、dispatch_group_leave()就是訊號量++ 4、dispatch_group_enter() 必須執行在 dispatch_group_leave() 之前。 5、dispatch_group_enter() 和 dispatch_group_leave() 需要成對出現的
//1.建立佇列組
dispatch_group_t group = dispatch_group_create();
//2.1.全域性佇列
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//2.2.主佇列
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//2.3.自建序列佇列
dispatch_group_async(group, dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//3.都完成後會自動通知,不阻塞當前執行緒
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
NSLog(@"當前執行緒=====%@",[NSThread currentThread]);
/*
並行佇列、自建序列佇列的任務多執行緒非同步執行
主佇列的任務主執行緒同步執行,且排在全部任務的最後
NSLog資訊
當前執行緒=====<NSThread: 0x60000007c240>{number = 1, name = main}
group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
完成 - <NSThread: 0x60000007c240>{number = 1, name = main}
*/
複製程式碼
#####手動標記group完成
- dispatch_group_enter(group)
- dispatch_group_leave(group);
######dispatch_group_notify(不阻塞)想當與把block任務加在最後
NSLog(@"start");
//1.建立佇列組
dispatch_group_t group = dispatch_group_create();
for (int i=0; i< 5; i++) {
dispatch_group_enter(group); // enter
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
NSLog(@"something===%zd",i);
dispatch_group_leave(group); // eave
});
}
// 都完成後會自動通知,不阻塞當前執行緒
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
NSLog(@"end");
/*
NSLog資訊:
start
end
something===1
something===0
something===2
something===3
something===4
完成 - <NSThread: 0x60800006e900>{number = 1, name = main}
*/
複製程式碼
######dispatch_group_wait就是等待group的訊號量回到初始值(阻塞當前執行緒)
NSLog(@"start");
//1.建立佇列組
dispatch_group_t group = dispatch_group_create();
for (int i=0; i< 5; i++) {
dispatch_group_enter(group); // enter
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
[NSThread sleepForTimeInterval:2];
NSLog(@"something===%zd",i);
dispatch_group_leave(group); // eave
});
}
// 阻塞當前執行緒的、等待5秒
dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_group_wait(group, waitTime);//特殊值有:DISPATCH_TIME_FOREVER是無限等,DISPATCH_TIME_NOW是不等
NSLog(@"end");
/*
等待時間 < 執行需要時間
NSLog資訊:
start
end
something===0
something===1
something===3
something===2
something===4
*/
/*
等待時間 > 執行需要時間
NSLog資訊:
start
something===1
something===0
something===4
something===2
something===3
end
*/
複製程式碼
#####dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait 訊號量是控制任務執行的重要條件,當訊號量為0時,所有任務等待,訊號量越大,允許可並行執行的任務數量越多。
- dispatch_semaphore_create(long value);建立訊號量,初始值不能小於0;value訊號數值
- dispatch_semaphore_wait(semaphore, timeout);等待降低訊號量,也就是訊號量-1;timeout不是呼叫dispatch_semaphore_wait後等待的時間,而是訊號量建立後的時間
- dispatch_semaphore_signal(semaphore);提高訊號量,也就是訊號量+1;
- dispatch_semaphore_wait和dispatch_semaphore_signal通常配對使用。
// 相當於控制新建的執行緒數
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
for (int i=0; i< 10; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
}
/*
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
只有5條執行緒
NSLog資訊:
第0次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
第1次_<NSThread: 0x600000269240>{number = 6, name = (null)}
第3次_<NSThread: 0x600000269100>{number = 5, name = (null)}
第2次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
第4次_<NSThread: 0x600000269780>{number = 7, name = (null)}
第8次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
第7次_<NSThread: 0x600000269100>{number = 5, name = (null)}
第6次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
第5次_<NSThread: 0x600000269240>{number = 6, name = (null)}
第9次_<NSThread: 0x600000269780>{number = 7, name = (null)}
*/
/*
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
10條執行緒
NSLog資訊:
第2次_<NSThread: 0x6080000661c0>{number = 3, name = (null)}
第4次_<NSThread: 0x608000073e40>{number = 7, name = (null)}
第1次_<NSThread: 0x600000079dc0>{number = 4, name = (null)}
第5次_<NSThread: 0x6000000721c0>{number = 8, name = (null)}
第3次_<NSThread: 0x608000073dc0>{number = 6, name = (null)}
第0次_<NSThread: 0x608000073d40>{number = 5, name = (null)}
第6次_<NSThread: 0x608000073d80>{number = 9, name = (null)}
第9次_<NSThread: 0x608000073e00>{number = 10, name = (null)}
第7次_<NSThread: 0x6000000717c0>{number = 11, name = (null)}
第8次_<NSThread: 0x600000066b40>{number = 12, name = (null)}
*/
複製程式碼
#####dispatch_barrier_async、dispatch_barrier_sync (承上啟下--用於自建的並行佇列) 保證此前的任務都先於自己執行,此後的任務也遲於自己執行。 dispatch_barrier_async 不阻塞當前執行緒; dispatch_barrier_sync 阻塞當前執行緒;
注意:dispatch_barrier_(a)sync只在自己建立的併發佇列上有效,在全域性(Global)併發佇列、序列佇列上,效果跟dispatch_(a)sync效果一樣。
- (void)test{
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
NSLog(@"任務1");
});
dispatch_async(globalQueue, ^{
NSLog(@"任務2");
});
dispatch_barrier_async(globalQueue, ^{
NSLog(@"任務barrier");
});
dispatch_async(globalQueue, ^{
NSLog(@"任務3");
});
dispatch_async(globalQueue, ^{
NSLog(@"任務4");
});
/*
2017-09-02 21:03:40.255 NSThreadTest[28856:21431532] 任務2
2017-09-02 21:03:40.255 NSThreadTest[28856:21431535] 任務1
2017-09-02 21:03:40.255 NSThreadTest[28856:21431533] 任務barrier
2017-09-02 21:03:40.255 NSThreadTest[28856:21431551] 任務3
2017-09-02 21:03:40.256 NSThreadTest[28856:21431550] 任務4
*/
}
複製程式碼
GCD建立Timer
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//建立佇列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.建立一個GCD定時器
/*
第一個引數:表明建立的是一個定時器
第四個引數:佇列
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 需要對timer進行強引用,保證其不會被釋放掉,才會按時呼叫block塊
// 區域性變數,讓指標強引用
self.timer = timer;
//2.設定定時器的開始時間,間隔時間,精準度
/*
第1個引數:要給哪個定時器設定
第2個引數:開始時間
第3個引數:間隔時間
第4個引數:精準度 一般為0 在允許範圍內增加誤差可提高程式的效能
GCD的單位是納秒 所以要*NSEC_PER_SEC
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3.設定定時器要執行的事情
dispatch_source_set_event_handler(timer, ^{
NSLog(@"---%@--",[NSThread currentThread]);
});
// 啟動
dispatch_resume(timer);
}
複製程式碼
#####GCD各種死鎖的情況
######1、最常見的
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"0========%@",[NSThread currentThread]);// 當前是主執行緒、主佇列
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
NSLog(@"2========%@",[NSThread currentThread]);
/*
NSLog資訊
0========<NSThread: 0x60000007c5c0>{number = 1, name = main}
解析
1.viewDidLoad是主佇列,dispatch_sync也屬於主佇列,
2.dispatch_sync是viewDidLoad裡面的程式碼,viewDidLoad需要等待dispatch_sync執行完,dispatch_sync需要等待viewDidLoad執行完,這就死鎖了
*/
}
複製程式碼
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"0========%@",[NSThread currentThread]);// 當前是主執行緒、主佇列
// 改成dispatch_get_global_queue或者new出來的佇列
dispatch_sync(dispatch_get_global_queue(0,0), ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
NSLog(@"2========%@",[NSThread currentThread]);
/*
NSLog資訊
0========<NSThread: 0x6000000690c0>{number = 1, name = main}
1========<NSThread: 0x6000000690c0>{number = 1, name = main}
2========<NSThread: 0x6000000690c0>{number = 1, name = main}
解析
1.viewDidLoad是主佇列,dispatch_sync是global_queue,不在同一佇列
2.dispatch_sync是viewDidLoad裡面的程式碼,viewDidLoad需要等待dispatch_sync執行完返回,但是dispatch_sync不需要等待viewDidLoad執行完,立即執行完返回
*/
}
複製程式碼
######2、序列佇列,各種巢狀非同步情況 死鎖的原因:是同一個序列佇列任務內部程式碼繼續巢狀同步sync的任務
// 序列佇列
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
// 同步嵌非同步----執行OK
dispatch_sync(queue, ^{
dispatch_async(queue, ^{
});
});
// 非同步嵌非同步----執行OK
dispatch_async(queue, ^{
dispatch_async(queue, ^{
});
});
// 非同步嵌同步----死鎖
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
});
});
// 同步嵌同步----死鎖
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
});
});
複製程式碼
######3、並行佇列,各種巢狀非同步情況 並行佇列佇列各種巢狀都不會死鎖
// 並行佇列
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
// 同步嵌非同步----執行OK
dispatch_sync(queue, ^{
dispatch_async(queue, ^{
});
});
// 非同步嵌非同步----執行OK
dispatch_async(queue, ^{
dispatch_async(queue, ^{
});
});
// 非同步嵌同步----執行OK
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
});
});
// 同步嵌同步----執行OK
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
});
});
複製程式碼
######4、dispatch_apply阻塞當前執行緒
// 主佇列使用,死鎖
dispatch_apply(5, dispatch_get_main_queue(), ^(size_t i) {
NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
});
複製程式碼
// 巢狀使用,死鎖
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t) {
// 任務
...
dispatch_apply(10, queue, ^(size_t) {
// 任務
...
});
});
複製程式碼
dispatch_barrier dispatch_barrier_sync在序列佇列和全域性並行佇列裡面和dispatch_sync同樣的效果,所以需考慮同dispatch_sync一樣的死鎖問題。
######5、 訊號量阻塞主執行緒
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"semaphore create!");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore);
NSLog(@"semaphore plus 1");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");
}
複製程式碼
原因: 如果當前執行的執行緒是主執行緒,以上程式碼就會出現死鎖。 因為dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)阻塞了當前執行緒,而且等待時間是DISPATCH_TIME_FOREVER——永遠等待,這樣它就永遠的阻塞了當前執行緒——主執行緒。導致主線中的dispatch_semaphore_signal(semaphore)沒有執行, 而dispatch_semaphore_wait一直在等待dispatch_semaphore_signal改變訊號量,這樣就形成了死鎖。
解決方法: 應該將訊號量移到並行佇列中,如全域性排程佇列。以下場景,移到序列佇列也是可以的。但是序列佇列還是有可能死鎖的(如果執行dispatch_semaphore_signal方法還是在對應序列佇列中的話,即之前提到的序列佇列巢狀序列佇列的場景)。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"semaphore create!");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore); // +1
NSLog(@"semaphore plus 1");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");
});
}
複製程式碼
#####一些巢狀使用問題
NSLog(@"1");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_async(dispatch_get_main_queue(), ^{
sleep(1);
NSLog(@"3");
});
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"4");
});
NSLog(@"5");
//結果是:12534
//解析:“2”併發佇列同步任務,所以125;“3”、“4”是兩個主佇列非同步,序列執行任務34;最終就是12534
複製程式碼
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"B");
});
NSLog(@"A");
});
// 結果是:BA (併發佇列不會死鎖) 並行佇列,任務A加入佇列執行中,然後任務B加入佇列也立即執行,但是任務A會等任務B先執行完。
複製程式碼