文章分享至個人我的技術博客: https://cainluo.github.io/15061581251764.htmlhtml
這篇文章主要是給以前的GCD
文章做爲一個補充, 順便講講一些在實際開發中遇到的問題和一些解決的辦法, 若是沒有看過以前文章的朋友能夠去看看:git
轉載聲明:如須要轉載該文章, 請聯繫做者, 而且註明出處, 以及不能擅自修改本文.github
在咱們實際開發中, 何時會遇到線程死鎖呢? 估計有不少小夥伴就能夠想到了, 就是在主隊列裏同步執行不一樣線程的時候:vim
- (void)mineQueueLockThread {
dispatch_queue_t queue = dispatch_queue_create("mineQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"第一次執行, %@", [NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"第二次執行, %@", [NSThread currentThread]);
});
});
}
複製代碼
打印結果:bash
2017-09-23 17:40:05.147 GCD-Tips[56843:3169265] 開始執行
2017-09-23 17:40:05.147 GCD-Tips[56843:3169265] 第一次執行, <NSThread: 0x60000007c8c0>{number = 1, name = main}
(lldb)
複製代碼
看到上面的圖, 咱們就能夠看到任務執行到這裏就死掉了, 也就是咱們所說的卡線程, 那咱們改改吧, 有的朋友會想到, 我把裏面的那個任務改爲異步不就行了麼? 那咱們來看看:微信
- (void)mineQueueLockThread {
dispatch_queue_t queue = dispatch_queue_create("mineQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"第一次執行, %@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"第二次執行, %@", [NSThread currentThread]);
});
});
}
複製代碼
打印結果:app
2017-09-23 17:44:12.488 GCD-Tips[56903:3176227] 開始執行
2017-09-23 17:44:12.488 GCD-Tips[56903:3176227] 第一次執行, <NSThread: 0x60000006ac40>{number = 1, name = main}
2017-09-23 17:44:12.489 GCD-Tips[56903:3176345] 第二次執行, <NSThread: 0x600000075ec0>{number = 3, name = (null)}
複製代碼
事實告訴咱們這是正常的, 那還有另外一種呢? 直接改外部的任務爲異步執行怎麼樣?異步
- (void)mineQueueLockThread {
dispatch_queue_t queue = dispatch_queue_create("mineQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"第一次執行, %@", [NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"第二次執行, %@", [NSThread currentThread]);
});
});
}
複製代碼
2017-09-23 17:45:52.740 GCD-Tips[56936:3178609] 開始執行
2017-09-23 17:45:52.741 GCD-Tips[56936:3178739] 第一次執行, <NSThread: 0x600000070640>{number = 3, name = (null)}
(lldb)
複製代碼
看到結果, 掛了, 爲何呢? 按道理來講, 外部是異步, 而裏面是同步是不會卡死的, 其實在以前的文章裏咱們就提到過.async
首先, 咱們知道第一個是異步執行的, 這是沒有問題的, 問題就是在第二個任務, 咱們都知道這裏是在主線程中執行, 可是別忘了, 主線程正在執行着任務, 因此這時候咱們再去執行, 那問題就出現了, 因此就卡死了啦~ui
解決的辦法, 有兩個:
PS: 這裏不要在同步執行嵌套串行隊列, 哪怕你是分開小方法裏也是同樣的.
當咱們不恰當的時候dispatch_apply
的時候也會形成死鎖的狀況:
- (void)applyLockThread {
dispatch_queue_t queue = dispatch_queue_create("applyQueue", DISPATCH_QUEUE_SERIAL);
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"延遲添加一");
dispatch_apply(3, queue, ^(size_t j) {
NSLog(@"延遲添加二");
});
});
}
複製代碼
打印的結果:
2017-09-24 00:13:10.760 GCD-Tips[57745:3330022] 開始執行
2017-09-24 00:13:10.761 GCD-Tips[57745:3330022] 延遲添加一
(lldb)
複製代碼
這裏咱們是串行隊列, 因此大體道理和上面的差很少, 那怎麼解決呢? 咱們能夠把串行隊列, 改爲並行隊列:
- (void)applyLockThread {
dispatch_queue_t queue = dispatch_queue_create("applyQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@"延遲添加一");
dispatch_apply(3, queue, ^(size_t j) {
NSLog(@"延遲添加二");
});
});
}
複製代碼
2017-09-24 00:13:58.238 GCD-Tips[57777:3331061] 開始執行
2017-09-24 00:13:58.239 GCD-Tips[57777:3331061] 延遲添加一
2017-09-24 00:13:58.239 GCD-Tips[57777:3331183] 延遲添加一
2017-09-24 00:13:58.239 GCD-Tips[57777:3331061] 延遲添加二
2017-09-24 00:13:58.239 GCD-Tips[57777:3331198] 延遲添加一
2017-09-24 00:13:58.239 GCD-Tips[57777:3331061] 延遲添加二
2017-09-24 00:13:58.239 GCD-Tips[57777:3331183] 延遲添加二
2017-09-24 00:13:58.240 GCD-Tips[57777:3331061] 延遲添加二
2017-09-24 00:13:58.240 GCD-Tips[57777:3331198] 延遲添加二
2017-09-24 00:13:58.240 GCD-Tips[57777:3331183] 延遲添加二
2017-09-24 00:13:58.240 GCD-Tips[57777:3331198] 延遲添加二
2017-09-24 00:13:58.240 GCD-Tips[57777:3331183] 延遲添加二
2017-09-24 00:13:58.240 GCD-Tips[57777:3331198] 延遲添加二
複製代碼
以前咱們在文章裏有提過使用dispatch_apply
, 這是一個延遲提交任務到線程中執行的方法, 除了這個任務延遲提交以外, 在隊列組當中也有一個對應的方法, 叫作dispatch_after
.
- (void)groupQueueAfter {
dispatch_time_t timeOne = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
dispatch_time_t timeTwo = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_after(timeOne, mainQueue, ^{
NSLog(@"延遲添加一");
});
dispatch_after(timeTwo, mainQueue, ^{
NSLog(@"延遲添加二");
});
}
複製代碼
打印的結果:
2017-09-24 00:28:35.205 GCD-Tips[57882:3349738] 開始執行
2017-09-24 00:28:37.405 GCD-Tips[57882:3349738] 延遲添加一
2017-09-24 00:28:38.504 GCD-Tips[57882:3349738] 延遲添加二
複製代碼
從打印中, 咱們能夠看得出的確是延遲添加了, 但這裏須要注意一點, dispatch_after
只是延遲提交Block
而已, 並非延後當即執行, 不能作到精準控制的.
這裏解釋一下dispatch_time
的第二個參數:
#define NSEC_PER_SEC 1000000000ull
#define NSEC_PER_MSEC 1000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
複製代碼
若是咱們要延遲1秒的話, 能夠有幾種寫法:
dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);
複製代碼
最後一個USEC_PER_SEC * NSEC_PER_USEC
,翻譯過來就是每秒的毫秒數乘以每毫秒的納秒數
,也就是每秒的納秒數
,因此,延時500毫秒
之類的,也就不難了吧~
在這裏, dispatch_after
是能夠嵌套使用的, 但須要注意的是, 雖然並不會形成鎖線程, 但會致使延遲提交Block
提早:
- (void)groupQueueAfterNested {
dispatch_time_t timeOne = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
dispatch_time_t timeTwo = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_after(timeOne, mainQueue, ^{
NSLog(@"延遲添加一");
dispatch_after(timeTwo, mainQueue, ^{
NSLog(@"延遲添加二");
});
});
}
複製代碼
2017-09-24 00:31:16.810 GCD-Tips[57923:3354999] 開始執行
2017-09-24 00:31:19.010 GCD-Tips[57923:3354999] 延遲添加一
2017-09-24 00:31:19.885 GCD-Tips[57923:3354999] 延遲添加二
複製代碼
因此咱們在這裏使用dispatch_after
的時候, 要注意延遲添加的時機了.
在以前的文章裏, 咱們有提過dispatch_apply
, 這裏補充一點就是它在使用的時候, 不管是串行仍是並行隊列都同樣, 它將會阻塞主線程
, 咱們來看代碼:
- (void)queueApply {
NSLog(@"開始執行, 當前線程爲:%@", [NSThread currentThread]);
dispatch_queue_t queueOne = dispatch_queue_create("applyQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queueTwo = dispatch_queue_create("applyQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(3, queueOne, ^(size_t i) {
NSLog(@"延遲執行一: %zu, 當前線程爲:%@", i, [NSThread currentThread]);
});
dispatch_apply(3, queueTwo, ^(size_t i) {
NSLog(@"延遲執行二: %zu, 當前線程爲:%@", i, [NSThread currentThread]);
});
NSLog(@"結束執行, 當前線程爲:%@", [NSThread currentThread]);
}
複製代碼
2017-09-24 00:44:04.704 GCD-Tips[58065:3373668] 開始執行
2017-09-24 00:44:04.705 GCD-Tips[58065:3373668] 開始執行, 當前線程爲:<NSThread: 0x600000067880>{number = 1, name = main}
2017-09-24 00:44:04.705 GCD-Tips[58065:3373668] 延遲執行一: 0, 當前線程爲:<NSThread: 0x600000067880>{number = 1, name = main}
2017-09-24 00:44:04.706 GCD-Tips[58065:3373668] 延遲執行一: 1, 當前線程爲:<NSThread: 0x600000067880>{number = 1, name = main}
2017-09-24 00:44:04.706 GCD-Tips[58065:3373668] 延遲執行一: 2, 當前線程爲:<NSThread: 0x600000067880>{number = 1, name = main}
2017-09-24 00:44:04.706 GCD-Tips[58065:3373668] 延遲執行二: 0, 當前線程爲:<NSThread: 0x600000067880>{number = 1, name = main}
2017-09-24 00:44:04.706 GCD-Tips[58065:3373784] 延遲執行二: 1, 當前線程爲:<NSThread: 0x60800006f440>{number = 3, name = (null)}
2017-09-24 00:44:04.706 GCD-Tips[58065:3373786] 延遲執行二: 2, 當前線程爲:<NSThread: 0x60800006f380>{number = 4, name = (null)}
2017-09-24 00:44:04.707 GCD-Tips[58065:3373668] 結束執行, 當前線程爲:<NSThread: 0x600000067880>{number = 1, name = main}
複製代碼
好了, 此次就講到這裏, 一會兒講太多也很差消化, 剩下的留着以後再講吧.
項目地址: https://github.com/CainRun/iOS-Project-Example/tree/master/GCD-Tips/GCD-Tips-One