GCD 全稱Grand Central Dispatch,是純C語言,它會自動掛哪裏線程的生命週期。
多線程最基本的運行原理就是將任務添加到隊列,並且指定執行任務的函數,如下:
//1:創建串行隊列
dispatch_queue_t queue = dispatch_queue_create("QueueName", DISPATCH_QUEUE_SERIAL);
//2:創建任務
dispatch_block_t taskBlock = ^{
NSLog(@"%@",[NSThread currentThread]);
};
//3:利用函數把任務放入隊列
dispatch_sync(queue, taskBlock);
隊列分爲串行隊列和並行隊列 ,函數分爲 同步函數(dispatch_sync)和異步函數(dispatch_async),隊列和函數類型的組合,決定了能都開闢新的線程
- 同步:必須等待當前語句執行完畢,纔會執行下一條語句,不會開啓線程。
- 異步:不用等待當前語句執行完畢就可以執行下一條任務。
- a、並行隊列 + 異步函數 開啓多條線程,不按順序執行任務;
- b、串行隊列 + 異步函數 開啓一條新線程,按順序執行任務;
- c、主隊列 + 異步函數不開啓新線程,按順序執行任務
- d、並行隊列 + 同步函數不開啓新線程,按順序執行任務;
- e、串行隊列 + 同步函數不開啓新線程,按順序執行任務;
- f、主隊列 + 同步函數會出現卡死現象!原因:循環等待,主隊列的東西要等主線程執行完,又不能開線程,所以下面的任務要等上面的任務執行完,然後卡死
關於函數與隊列的組合Demo,點擊此處下載~
還有一種情況的死鎖
執行順序如圖所示, 當執行到sync_block的時候,由於是一個同步函數調用的串行隊列,同步函數必須要先執行它block 裏邊的任務3,但是串行隊列FIFO原則,3必須等到 4執行後才能執行, 雙方相互等待,形成死鎖。
關於主隊列死鎖
NSLog(@"0");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"1");
});
NSLog(@"2");
同步函數調用主隊列(主隊列不是串行隊列但是類似串行隊列),同樣會造成死鎖。
柵欄函數
當我們的任務有依賴關係的時候,比如任務1和2執行完畢後才能執行任務3和4,這時候我們可以用到這個函數——柵欄函數。其中 queue 是隊列,block 是任務。
dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t blcok)
提交一個柵欄函數在同步執行中,它會等待柵欄函數執行完再去執行下一行代碼,同步柵欄函數是在主線程中執行的。
dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t blcok)
提交一個柵欄函數在異步執行中,它會立馬返回開始執行下一行代碼(不用等待任務執行完畢)。
前提: 必須是同一個隊列(queue)
注意點: 柵欄函數不能封裝異步網絡請求,因爲異步網絡請求不能保證任務在同一個隊列上。
共同點:
- 都會等待自己前邊插入的隊列的任務(1、2、3)先執行完。
- 都會等待他們自己的任務(barrier)執行完後再執行後的任務。
不同點
- dispatch_barrier_sync 需要等待自己的任務(barrier)結束之後,纔會繼續添加並執行寫在barrier後邊的任務,然後執行。
- dispatch_barrier_async將自己的任務(barrier)插入到隊列之後,不用等待自己任務的結束,會繼續把後邊的任務添加到隊列中,然後開始執行。
調度組
我們經常需要需要監聽多個異步任務全部完成後的回調。此時需要用到調度組處理
注意點: 必須是同一個組group
場景:使用異步下載或者請求的時候,等多個任務都執行完時, dispatch_group_notify()監聽統一的回調。
基本的調度組執行如下:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4 repeats:NO block:^(NSTimer * _Nonnull timer) {
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop]run];
});
dispatch_group_async(group, queue, ^{
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer * _Nonnull timer) {
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop]run];
});
dispatch_group_wait(group, 5); // 最長等待時間
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"--5^^ %@",[NSThread currentThread]); // <= 5秒調用
});
調度組內部方法 enter - leave
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"--1^^ 執行完了,準備出組");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"--2^^ 執行完了,準備出組");
dispatch_group_leave(group);
});
dispatch_group_wait(group, 5);
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"--5^^ %@",[NSThread currentThread]);
});
dispatch_group_enter() 與 dispatch_group_leave 保證成對出現,
dispatch_group_leave 少於dispatch_group_enter(),將不會調用通知, 反之crash;
信號量
信號量是用在多線程併發的可控最大任務數量的操作,一個線程完成了某一個動作就通過信號量告訴別的線程,別的線程再進行某些動作
// 創建一個信號,value:信號量
dispatch_semaphore_create(<#long value#>)
// 使某個信號的信號量+1
dispatch_semaphore_signal(<#dispatch_semaphore_t dsema#>)
// 某個信號進行等待或等待降低信號量 timeout:等待時間,永遠等待爲 DISPATCH_TIME_FOREVER
dispatch_semaphore_wait(<#dispatch_semaphore_t dsema#>, <#dispatch_time_t timeout#>)
正常的使用順序是先降低然後再提高,這兩個函數通常成對使用。
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); // 同時執行任務的數量,不超過2個
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"執行任務1");
sleep(1);
NSLog(@"任務1完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"執行任務2");
sleep(1);
NSLog(@"任務2完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"執行任務3");
sleep(1);
NSLog(@"任務3完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_source
關於source源的設置, 1、2、3 爲創建, 4、5、6爲設置。
dispatch_source_create
1、創建source源
dispatch_source_set_event_handler
2、設置回調
dispatch_source_merge_data
3、引發回調(綁定source, 帶參data)
dispatch_source_get_data
4、獲取source綁定的data
dispatch_suspend
5、掛起
dispatch_resume
6.繼續