GCD兩個基本知識:任務和隊列;
任務執行方式分爲兩種:同步(sync)和異步(async) 隊列也分爲兩種:併發隊列和串行隊列; 各類組合方式: 異步函數+併發隊列:開啓多條線程,併發執行任務 異步函數+串行隊列:開啓一條線程,串行執行任務 同步函數+併發隊列:不開線程,串行執行任務 同步函數+串行隊列:不開線程,串行執行任務 異步函數+主隊列:不開線程,在主線程中串行執行任務 同步函數+主隊列:不開線程,串行執行任務(發生死鎖)
GCD基本函數:
dispatch_barrier_async/dispatch_group_notify/dispatch_semaphore_wait 不會阻塞線程 dispatch_group_wait 會阻塞線程
dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ for(int i = 0; i < 5; i++){ NSLog(@"11111111---%@", [NSThread currentThread]); } }); dispatch_barrier_async(queue, ^{ NSLog(@"--dispatch_barrier_async-"); }); dispatch_async(queue, ^{ for(int i = 0; i < 5; i++){ NSLog(@"222222---%@", [NSThread currentThread]); } }); 執行結果: testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)} testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)} testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)} testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)} testNSThread[1563:634368] 11111111---<NSThread: 0x15457e3b0>{number = 3, name = (null)} testNSThread[1563:634366] --dispatch_barrier_async- testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)} testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)} testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)} testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)} testNSThread[1563:634369] 222222---<NSThread: 0x154536fe0>{number = 4, name = (null)}
dispatch_barrier_async並不會阻塞線程,同一個併發隊列中的操做等待dispatch_barrier_async執行完以後纔會執行,其餘操做不會等dispatch_barrier_async執行完以後再執行;多線程
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"task1"); }); dispatch_async(queue, ^{ NSLog(@"task2"); }); dispatch_barrier_async(queue, ^{ NSLog(@"barrier_async"); }); dispatch_async(queue, ^{ NSLog(@"task3"); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"task4"); }); NSLog(@"---end"); 執行結果: 2017-10-12 10:16:29.368495+0800 test[5491:2328009] ---end 2017-10-12 10:16:29.370267+0800 test[5491:2328077] task1 2017-10-12 10:16:29.370539+0800 test[5491:2328733] task2 2017-10-12 10:16:29.370596+0800 test[5491:2328077] task4 2017-10-12 10:16:29.370677+0800 test[5491:2328733] barrier_async 2017-10-12 10:16:29.370795+0800 test[5491:2328733] task3 能夠看到task4和輸出end 不會等barrier_async執行完再執行;
dispatch_after函數不是延遲N秒再執行block,而是在N秒以後纔將block加入到隊列中去; 併發
延遲時間有兩種計算方式: 一、相對時間: dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC) 表示在當前時間2秒後 DISPATCH_TIME_NOW:當前時間 NSEC_PER_SEC 表示秒數 NSEC_PER_MSEC表示毫秒 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"---%@",[NSThread currentThread]); }); 二、絕對時間:絕對時間須要用 dispatch_walltime 函數建立; dispatch_walltime(const struct timespec *_Nullable when, int64_t delta); 第一個參數是timespec結構體, _STRUCT_TIMESPEC { __darwin_time_t tv_sec; //秒數的整數部分 long tv_nsec; //秒數的小數部分 }; 示例代碼: struct timespec spec = {0, 0}; //獲取當前時間作測試 NSDate *date = [NSDate date]; //計算當前時間到1970的秒數 NSTimeInterval timeInterval = [date timeIntervalSince1970]; timeInterval += 5; //在當前時間延遲5秒執行 double second = 0; //秒數的整數部分 double subSecond = 0; //秒數的小數部分 //modf函數的做用是求出整數部分和小數部分 subSecond = modf(timeInterval, &second); spec.tv_sec = second; spec.tv_nsec = subSecond * NSEC_PER_SEC; dispatch_time_t time2= dispatch_walltime(&spec, 0); dispatch_after(time2, dispatch_get_global_queue(0, 0), ^{ NSLog(@"111111"); });
dispatch_once函數一般用在單例模式上,它能夠保證在程序運行期間某段代碼只執行一次 app
-(void)once { //整個程序運行過程當中只會執行一次 //onceToken用來記錄該部分的代碼是否被執行過 static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"-----"); }); }
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) { NSLog(@"%zd", index); }); 輸出結果: 2017-04-27 11:13:59.439 newTest[3563:1745064] 1 2017-04-27 11:13:59.439 newTest[3563:1745019] 0 2017-04-27 11:13:59.440 newTest[3563:1745019] 3 2017-04-27 11:13:59.440 newTest[3563:1745064] 2 2017-04-27 11:13:59.441 newTest[3563:1745064] 5 2017-04-27 11:13:59.441 newTest[3563:1745019] 4 2017-04-27 11:13:59.441 newTest[3563:1745064] 6 2017-04-27 11:13:59.441 newTest[3563:1745064] 8 2017-04-27 11:13:59.441 newTest[3563:1745019] 7 2017-04-27 11:13:59.441 newTest[3563:1745064] 9
下面有兩種方式來實現 等待某些操做完成後纔開始執行後續操做, 方法1: dispatch_group_notify:當 dispatch_group中全部的block都執行完後, dispatch_group_notify會通知隊列任務都執行完畢,並執行 dispatch_group_notify 的block; dispatch_group_notify不會阻塞隊列; dispatch_group_wait : 同步的等待隊列中的任務都執行完畢,纔會執行後續的任務;會阻塞隊列; dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create("com.test", DISPATCH_QUEUE_CONCURRENT); dispatch_group_async(group, queue, ^{ for (NSInteger i = 0; i<10; i++) { NSLog(@"%zd-download1--%@",i,[NSThread currentThread]); } }); dispatch_group_async(group, queue, ^{ for (NSInteger i = 0; i<10; i++) { NSLog(@"%zd-download2--%@",i,[NSThread currentThread]); } }); dispatch_group_notify(group, queue, ^{ NSLog(@"dispatch_group_notify"); }); 方法2:dispatch_group_enter、dispatch_group_wait dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); NSLog(@"group enter"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ for(int i = 0; i < 10; i++){ NSLog(@"%zd----%@", i, [NSThread currentThread]); } NSLog(@"group leave"); dispatch_group_leave(group); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"another task"); }); 須要注意的是,dispatch_group_wait實際上會使當前的線程處於等待的狀態, 也就是說若是是在主線程執行dispatch_group_wait,在上面的Block執行完以前,主線程會處於卡死的狀態。 能夠注意到dispatch_group_wait的第二個參數是指定超時的時間, 若是指定爲DISPATCH_TIME_FOREVER(如上面這個例子)則表示會永久等待,直到上面的Block所有執行完, 除此以外,還能夠指定爲具體的等待時間,根據dispatch_group_wait的返回值來判斷是上面block執行完了仍是等待超時了。 最後,同以前建立dispatch_queue同樣,若是是在OS X 10.8或iOS 6以及以後版本中使用, Dispatch Group將會由ARC自動管理,若是是在此以前的版本,須要本身手動釋放。
信號量在多線程開發中被普遍使用,當一個線程在進入一段關鍵代碼以前,線程必須獲取一個信號量,一旦該關鍵代碼段完成了,那麼該線程必須釋放信號量。其它想進入該關鍵代碼段的線程必須等待前面的線程釋放信號量。
信號量的具體作法是:當信號計數大於0時,每條進來的線程使計數減1,直到變爲0,變爲0後其餘的線程將進不來,處於等待狀態;執行完任務的線程釋放信號,使計數加1,如此循環下去。異步
下面這個例子中使用了10條線程,可是同時只執行一條,其餘的線程處於等待狀態: //dispatch_semaphore_wait :減小信號量計數.若是結果小於0, 等待信號喚醒. //dispatch_semaphore_signal : 增長信號量計數,若是以前的值小於0 喚醒 dispatch_semaphore_wait //第一步:初始信號量= 0 semaphore = dispatch_semaphore_create(0); dispatch_async(dispatch_get_global_queue(0, 0), ^{ for(NSInteger i = 0; i < 10; i++){ NSLog(@"--------------%zd", i); } //第三步:任務執行完 判斷信號量小於0 喚醒dispatch_semaphore_wait dispatch_semaphore_signal(semaphore); }); //第二步:信號量減1 變成 -1,小於0 等待喚醒 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"dddddddddddddddddd"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ }); 取得信號量的線程在2秒後釋放了信息量,至關因而每2秒執行一次。 經過上面的例子能夠看到,在GCD中,用dispatch_semaphore_create函數能初始化一個信號量,同時須要指定信號量的初始值; 使用dispatch_semaphore_wait函數分配信號量並使計數減1,爲0時處於等待狀態; 使用dispatch_semaphore_signal函數釋放信號量,並使計數加1。 另外dispatch_semaphore_wait一樣也支持超時,只須要給其第二個參數指定超時的時候便可, 同Dispatch Group的dispatch_group_wait函數相似,能夠經過返回值來判斷。 這個函數也須要注意,若是是在OS X 10.8或iOS 6以及以後版本中使用,Dispatch Semaphore將會由ARC自動管理, 若是是在此以前的版本,須要本身手動釋放。