什么是GCD
GCD是蘋果對多線程編程做的一套新的抽象基于C語言層的API,結合Block簡化了多線程的操作,使得我們對線程操作能夠更加的安全高效。
在GCD出現之前Cocoa框架提供了NSObject類的
performSelectorInBackground:withObject
performSelectorOnMainThread
方法來簡化多線程編程技術。
GCD可以解決以下多線程編程中經常出現的問題:
1.數據競爭(比如同時更新一個內存地址)
2.死鎖(互相等待)
3.太多線程導致消耗大量內存
在iOS中,如果把需要消耗大量時間的操作放在主線程上面,會妨礙主線程中被稱為RunLoop的主循環的執行,從而導致不能更新用戶界面、應用程序的畫面長時間停滯等問題。
Dispatch Queue
Dispatch Queue是GCD中對于任務的抽象隊列(FIFO)執行處理。
queue分為兩種,
SERIAL_DISPATCH_QUEUE 等待現在執行中處理結束
CONCURRENT_DISPATCH_QUEUE 不等待現在執行中處理結束
換句話說也就是 SERIAL_DISPATCH_QUEUE 是串行,CONCURRENT_DISPATCH_QUEUE是并行。
具體到線程上,就是SERIAL_DISPATCH_QUEUE只會創在一個線程來處理任務序列,而CONCURRENT_DISPATCH_QUEUE則會創在多個線程,但是具體創建多少個則是有運行的操作系統根據資源決定的。
所以SERIAL_DISPATCH_QUEUE 中處理的代碼是有序的,而CONCURRENT_DISPATCH_QUEUE中則是無序的,但是相對會更高效一點。
API
dispatch_queue_create
用于創建一個任務執行queue
參數列表
const char *label queue的名稱,作為該queue的唯一標示,改名會在Xcode和Instruments的調試器中直接作為DispatchQueue名稱顯示出來
dispatch_queue_attr_t 設定queue的類型,即ConcurrentQueue還是SerialQueue,NULL則默認為SerialQueue
返回值
dispatch_queue_t變量
這里要說一下main_dispatch_queue 和 global_dispatch_queue 這兩種系統提供的,
main_queue通過
dispatch_get_main_queue()
global_queue通過
dispatch_get_global_queue(),global等級分為
HIGH、DEFAULT、LOW、BACKGROUND四種
dispatch_async
向指定的queue中添加block操作,異步的執行,屏蔽了多線程的實現細節,自動為我們生成線程執行。
dispatch_after
類似延遲函數,可以指定queue來進行延遲操作
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC); dispatch_after(time, dispatch_get_main_queue(), ^{ NSLog(@"等待3秒"); });
dispatch_group_notify
對于監聽queue的執行,當所有任務完成后可以進行回調操作
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSLog(@"1"); }); dispatch_group_async(group, queue, ^{ NSLog(@"2"); }); dispatch_group_async(group, queue, ^{ NSLog(@"3"); }); dispatch_group_notify(group, queue, ^{ NSLog(@"finish"); });
對于一系列的block在同一queue中執行,如果是serialQueue是順序進行的,因此可以在最后一個任務來處理結束操作。但是對于concurrentQueue是并行的,如果想監聽完結操作,就要用該方法。
dispatch_group_wait和notify差不多,只不過wait方法可以設置等待時間。如果時間到了還沒有結束queue的所有操作,那么接下來還是會繼續進行,不過還是可以設定為forever一直等待下去,這樣就和notify起到一樣的作用。
dispatch_barrier_async
該操作主要是為了防止資源競爭。在concurrentQueue中,所有block無序的按照所創建的線程數量同時進行。如果在concurrentQueue中有兩個寫入操作,而且他都是讀取操作,這時兩個寫入操作間就會出現資源競爭,而讀取操作則會讀取臟數據。所以對于在concurrentQueue中不能夠與其它操作并行的block就需要使用dispatch_barrier_async方法來防止資源競爭。
dispatch_sync
和dispatch_async不同,dispatch_sync用于線程之間的同步操作,比如說A線程要做一件事必須要放在B線程之后來進行,那么此時就需要用到dispatch_sync。
另外,不能夠在某個執行線程中同步自己,這樣會造成線程死鎖,比如說
dispatch_queue_t queue1 = dispatch_get_main_queue(); dispatch_sync(queue1, ^{ NSLog(@"main queue 中同步main queue操作"); }); dispatch_queue_t queue = dispatch_queue_create("com.queue.www", NULL); dispatch_async(queue, ^{ dispatch_sync(queue, ^{ NSLog(@"在新的serial queue中同步serial queue操作"); }); });
所以說使用serial queue的時候一定不要同步自己。
dispatch_apply
dispatch_apply函數是dispatch_sync和dispatch group的關聯函數,是用指定的次數將指定的Block追加到指定的Dispatch Queue中,并等待全部處理執行結束,例如
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(10, queue, ^(size_t index) { NSLog(@"%ld",index); }); NSLog(@"apply finish");2015-08-02 09:38:18.296 Dispatch[7388:2035125] 42015-08-02 09:38:18.296 Dispatch[7388:2035244] 22015-08-02 09:38:18.296 Dispatch[7388:2035241] 12015-08-02 09:38:18.296 Dispatch[7388:2035259] 62015-08-02 09:38:18.296 Dispatch[7388:2035243] 02015-08-02 09:38:18.296 Dispatch[7388:2035257] 32015-08-02 09:38:18.296 Dispatch[7388:2035258] 52015-08-02 09:38:18.296 Dispatch[7388:2035260] 72015-08-02 09:38:18.296 Dispatch[7388:2035125] 82015-08-02 09:38:18.296 Dispatch[7388:2035244] 92015-08-02 09:38:18.296 Dispatch[7388:2035125] apply finish
實際上可以看出來,該函數讓主線程和queue進行同步操作,并且等queue中所有線程執行完畢后才繼續執行。
dispatch_semaphore
在進行數據處理時,dispatch_barrier_async可以避免這類問題,但是有時需要更加精細的操作。
比如要對數組添加10000個對象,用concurrentQueue添加。我們知道concurrentQueue會生成多個線程,很可能會出現多個線程一起對數組訪問的情況,很容易出現問題。我們需要控制一次只讓一個線程操作數組,如下:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); NSMutableArray *array = [NSMutableArray new]; for (int i = 0 ; i < 10000 ; i++) { dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); [array addObject:[NSNumber numberWithInt:i]]; NSLog(@"add %d",i); dispatch_semaphore_signal(semaphore); }); }
這里簡單說一下信號量,也就是創建dispatch_semaphore的第二個參數。指定一個信號量,那么當信號量是大于0的時候所有線程都是可訪問的。一旦有現成訪問信號量會減1,如果信號量為0就會進入等待,知道dispatch_semaphore_signal函數調用來重新恢復信號量。所以基本上可以理解為有幾個信號量就能有幾個線程并發的訪問。
再比如說現在有兩個線程一個添加數據一個刪除數據,那么就需要兩個信號量變量來實現多線程間的協作
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphoreAdd = dispatch_semaphore_create(1); dispatch_semaphore_t semaphoreRemove = dispatch_semaphore_create(0); NSMutableArray *array = [NSMutableArray new]; for (int i = 0 ; i < 2 ; i++) { dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphoreAdd, DISPATCH_TIME_FOREVER); [array addObject:[NSNumber numberWithInt:i]]; NSLog(@"add %lu",[array count]); dispatch_semaphore_signal(semaphoreRemove); }); dispatch_async(queue, ^{ dispatch_semaphore_wait(semaphoreRemove, DISPATCH_TIME_FOREVER); [array removeObject:[NSNumber numberWithInt:i]]; NSLog(@"add %lu",[array count]); dispatch_semaphore_signal(semaphoreAdd); }); }
dispatch_once
dispatch_once用來標記一個操作,只執行一次,該方法一般在生產單例對象使用。如果不用dispatch_once創建單例是不安全的,需要進行加鎖處理,但是dispatch_once可以很好地解決這一點。
+(instancetype)sharedInstance{ static CustomObject *obj; static dispatch_once_t once; dispatch_once(&once, ^{ obj = [[CustomObject alloc] init]; }); return obj;}
新聞熱點
疑難解答