GCD中兩個很是重要的概念: 任務 和 隊列數據庫
任務分爲同步執行sync和異步執行async, 同步和異步的區別在因而否會阻塞當前線程, 其實在GCD中一個任務就是一個block中的代碼.安全
隊列分爲串行隊列和並行隊列,主隊列dispatch_get_main_queue( )是串行隊列.多線程
咱們可使用dispatch_queue_create來建立新的隊列,異步
//串行隊列 dispatch_queue_t queue = dispatch_queue_create("testQueue", NULL); dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL); //並行隊列 dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
下面是我本身總結的:async
/ | 同步執行sync | 異步執行async |
---|---|---|
串行隊列 | 當前線程,一個一個執行 | 其餘一個線程, 一個一個執行 |
並行隊列 | 當前線程,一個一個執行 | 其餘一個或者多個線程(取決於任務數), 同時執行 |
經過代碼來驗證一下:函數
建立一個串行的隊列添加4個同步執行的任務spa
//串行隊列 dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ NSLog(@"111"); NSLog(@"111中 %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"222"); NSLog(@"222中 %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"333"); NSLog(@"333中 %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"444"); NSLog(@"444中 %@",[NSThread currentThread]); });
打印結果:線程
2018-03-09 15:34:26.474786+0800 Learning[3385:281473] 111 2018-03-09 15:34:26.475053+0800 Learning[3385:281473] 111中 <NSThread: 0x60000007a2c0>{number = 1, name = main} 2018-03-09 15:34:26.475165+0800 Learning[3385:281473] 222 2018-03-09 15:34:26.475369+0800 Learning[3385:281473] 222中 <NSThread: 0x60000007a2c0>{number = 1, name = main} 2018-03-09 15:34:26.475568+0800 Learning[3385:281473] 333 2018-03-09 15:34:26.476057+0800 Learning[3385:281473] 333中 <NSThread: 0x60000007a2c0>{number = 1, name = main} 2018-03-09 15:34:26.476249+0800 Learning[3385:281473] 444 2018-03-09 15:34:26.476351+0800 Learning[3385:281473] 444中 <NSThread: 0x60000007a2c0>{number = 1, name = main}
同步dispatch_sync改成異步dispatch_async
打印結果依然是有序, 可是開啓了一個子線程3d
2018-03-09 16:23:47.634329+0800 Learning[3830:331376] 111 2018-03-09 16:23:47.634739+0800 Learning[3830:331376] 111中 <NSThread: 0x604000274dc0>{number = 4, name = (null)} 2018-03-09 16:23:47.634911+0800 Learning[3830:331376] 222 2018-03-09 16:23:47.635262+0800 Learning[3830:331376] 222中 <NSThread: 0x604000274dc0>{number = 4, name = (null)} 2018-03-09 16:23:47.636508+0800 Learning[3830:331376] 333 2018-03-09 16:23:47.637206+0800 Learning[3830:331376] 333中 <NSThread: 0x604000274dc0>{number = 4, name = (null)} 2018-03-09 16:23:47.637413+0800 Learning[3830:331376] 444 2018-03-09 16:23:47.637680+0800 Learning[3830:331376] 444中 <NSThread: 0x604000274dc0>{number = 4, name = (null)}
若是把新建的隊列改成並行隊列, 同步執行code
//並行隊列 dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ NSLog(@"111"); NSLog(@"111中 %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"222"); NSLog(@"222中 %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"333"); NSLog(@"333中 %@",[NSThread currentThread]); }); dispatch_sync(queue, ^{ NSLog(@"444"); NSLog(@"444中 %@",[NSThread currentThread]); });
結果
2018-03-09 16:31:50.498599+0800 Learning[3938:341075] 111 2018-03-09 16:31:50.498836+0800 Learning[3938:341075] 111中 <NSThread: 0x600000063d80>{number = 1, name = main} 2018-03-09 16:31:50.498946+0800 Learning[3938:341075] 222 2018-03-09 16:31:50.499101+0800 Learning[3938:341075] 222中 <NSThread: 0x600000063d80>{number = 1, name = main} 2018-03-09 16:31:50.499227+0800 Learning[3938:341075] 333 2018-03-09 16:31:50.499353+0800 Learning[3938:341075] 333中 <NSThread: 0x600000063d80>{number = 1, name = main} 2018-03-09 16:31:50.499461+0800 Learning[3938:341075] 444 2018-03-09 16:31:50.499609+0800 Learning[3938:341075] 444中 <NSThread: 0x600000063d80>{number = 1, name = main}
把上面代碼中的同步改成異步, 即並行隊列, 異步執行
2018-03-09 16:34:13.089611+0800 Learning[3982:344351] 222 2018-03-09 16:34:13.089612+0800 Learning[3982:344350] 333 2018-03-09 16:34:13.089612+0800 Learning[3982:344349] 111 2018-03-09 16:34:13.089639+0800 Learning[3982:344352] 444 2018-03-09 16:34:13.089997+0800 Learning[3982:344350] 333中 <NSThread: 0x60000027d680>{number = 3, name = (null)} 2018-03-09 16:34:13.089997+0800 Learning[3982:344349] 111中 <NSThread: 0x604000271800>{number = 4, name = (null)} 2018-03-09 16:34:13.090004+0800 Learning[3982:344352] 444中 <NSThread: 0x604000271440>{number = 6, name = (null)} 2018-03-09 16:34:13.090031+0800 Learning[3982:344351] 222中 <NSThread: 0x604000271480>{number = 5, name = (null)}
從結果中能夠看得出, 開啓了四個不一樣的現成來執行四個任務.
注意
在同步+串行的時候會有一個特殊的狀況, 上面也提到了, 主隊列也是一個串行隊列, 若是當前在主線程中且把任務加到主隊列中會如何呢 ?
NSLog(@"---前 %@",[NSThread currentThread]); dispatch_sync(dispatch_get_main_queue(), ^{//dispatch_sync NSLog(@"---中 %@",[NSThread currentThread]); }); NSLog(@"---後 %@",[NSThread currentThread]);
運行結果造成死鎖.
思考: 一樣都是串行隊列, 爲何任務添加到主隊列會形成死鎖現象, 新建一個串行隊列則能夠正常執行 ?
解釋: 在執行完第一個NSLog的時候, 遇到dispatch_sync, 會暫時阻塞主線程. 那麼咱們想一下, 在阻塞以前, 主線程正在執行的是主隊列中的哪個任務呢? 用咱們實際的例子來解釋, 咱們再寫上面的代碼的時候, 這部分的代碼是包裹在一個另方法中的, 就像我本身寫的demo, 這段代碼就寫在viewDidLoad裏面, 由於viewDidLoad這些任務也是添加在主隊列中的, 因此說阻塞主線程時, 主線程應該是處理的主隊中 viewDidLoad這個任務.
咱們調用了dispatch_sync後向主隊列中添加了新的任務, 也就是dispatch_sync後面block中的代碼. 可是根據隊列的FIFO規則, 新添加的block中的任務, 確定是要排在主隊列的最後.
由於是使用的dispatch_sync同步, 因此說必需要等執行dispatch_sync的block中的代碼纔會返回, 纔會繼續執行ViewDidLoad中dispatch_sync這個方法下面的方法, 也就是代碼中的第三個NSLog, 可是block中的任務被放在了主隊列的底部, 他是不可能在viewDidLoad這個任務還沒完成的時候就執行到的, 因此就造成了兩個任務互相等待的狀況, 也就是造成了死鎖.
因此說這樣看來,GCD造成死鎖的緣由應該是是隊列阻塞,而不是線程阻塞.
解決先執行A和B, AB都執行完再執行C的這種狀況.
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); dispatch_group_async(group, queue, ^{ // 1 sleep(5); NSLog(@"task 1"); }); dispatch_group_async(group, queue, ^{ //2 NSLog(@"task 2"); }); dispatch_group_notify(group, queue, ^{ NSLog(@"done "); });
或者
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); dispatch_group_enter(group); dispatch_async(queue, ^{ NSLog(@"11"); dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_async(queue, ^{ sleep(7); NSLog(@"22"); dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_async(queue, ^{ NSLog(@"33"); dispatch_group_leave(group); }); dispatch_group_notify(group, queue, ^{ NSLog(@"done"); });
結果都是先執行完上面的在打印這個done, 這個不須要解釋了.
有時候可能會碰到dispatch_group_wait, 注意一下, 由於他會阻塞線程, 因此不能放在主線程裏面執行, 等待的時間是以group中任務開始執行的時間算起.
多線程最多見的問題就是讀寫,好比數據庫讀寫,文件讀寫,讀取是共享的,寫是互斥.
使用Concurrent Dispatch Queue和dispatch_barrier_basync函數可實現高效率的數據庫訪問和文件訪問。
若是容許多個線程進行讀操做,當寫文件時,阻止隊列中全部其餘的線程進入,直到文件寫完成.
在讀取文件時, 直接使用dispatch_async異步讀取, 當要寫文件是, 使用dispatch_barrier_async. 用代碼看一下dispatch_async和dispatch_barrier_async的執行順序.
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"task 1"); }); dispatch_async(queue, ^{ sleep(2); NSLog(@"task 2"); }); dispatch_barrier_async(queue, ^{ NSLog(@"barrier -- "); sleep(3); }); dispatch_async(queue, ^{ NSLog(@"task 3"); }); dispatch_async(queue, ^{ NSLog(@"task 4"); }); NSLog(@" end ");
結果是先打印完task1和2, 在打印完barrier, 最後打印task3和4, 同理到文件讀寫中, 文件讀取直接用dispatch_async, 寫入使用dispatch_barrier_async, 則在寫入文件時, 無論前面有多少操做都會等待前面的操做完成, 並且在寫入的時候, 也沒有其餘線程訪問, 從而達到線程安全.