什麼是GCD? 編程
Grand Central Dispatch或者GCD,是一套低層API,提供了一種新的方法來進行併發程序編寫。從基本功能上講,GCD有點像NSOperationQueue,他們都容許程序將任務切分爲多個單一任務而後提交至工做隊列來併發地或者串行地執行。GCD比之NSOpertionQueue更底層更高效,而且它不是Cocoa框架的一部分。 安全
除了代碼的平行執行能力,GCD還提供高度集成的事件控制系統。能夠設置句柄來響應文件描述符、mach ports(Mach port 用於 OS X上的進程間通信)、進程、計時器、信號、用戶生成事件。這些句柄經過GCD來併發執行。 多線程
GCD的API很大程度上基於block,固然,GCD也能夠脫離block來使用,好比使用傳統c機制提供函數指針和上下文指針。實踐證實,當配合block使用時,GCD很是簡單易用且能發揮其最大能力。 併發
你能夠在Mac上敲命令「man dispatch」來獲取GCD的文檔。 框架
爲什麼使用? 異步
GCD提供不少超越傳統多線程編程的優點: async
Dispatch Objects 函數
儘管GCD是純c語言的,但它被組建成面向對象的風格。GCD對象被稱爲dispatch object。Dispatch object像Cocoa對象同樣是引用計數的。使用dispatch_release和dispatch_retain函數來操做dispatch object的引用計數來進行內存管理。但主意不像Cocoa對象,dispatch object並不參與垃圾回收系統,因此即便開啓了GC,你也必須手動管理GCD對象的內存。 性能
Dispatch queues 和 dispatch sources(後面會介紹到)能夠被掛起和恢復,能夠有一個相關聯的任意上下文指針,能夠有一個相關聯的任務完成觸發函數。能夠查閱「man dispatch_object」來獲取這些功能的更多信息。 spa
Dispatch Queues
GCD的基本概念就是dispatch queue。dispatch queue是一個對象,它能夠接受任務,並將任務以先到先執行的順序來執行。dispatch queue能夠是併發的或串行的。併發任務會像NSOperationQueue那樣基於系統負載來合適地併發進行,串行隊列同一時間只執行單一任務。
GCD中有三種隊列類型:
建立隊列
要使用用戶隊列,咱們首先得建立一個。調用函數dispatch_queue_create就好了。函數的第一個參數是一個標籤,這純是爲了debug。Apple建議咱們使用倒置域名來命名隊列,好比「com.dreamingwish.subsystem.task」。這些名字會在崩潰日誌中被顯示出來,也能夠被調試器調用,這在調試中會頗有用。第二個參數目前還不支持,傳入NULL就好了。
提交 Job
向一個隊列提交Job很簡單:調用dispatch_async函數,傳入一個隊列和一個block。隊列會在輪到這個block執行時執行這個block的代碼。下面的例子是一個在後臺執行一個巨長的任務:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self goDoSomethingLongAndInvolved]; NSLog(@"Done doing something long and involved"); });
dispatch_async 函數會當即返回, block會在後臺異步執行。
固然,一般,任務完成時簡單地NSLog個消息不是個事兒。在典型的Cocoa程序中,你頗有可能但願在任務完成時更新界面,這就意味着須要在主線程中執行一些代碼。你能夠簡單地完成這個任務——使用嵌套的dispatch,在外層中執行後臺任務,在內層中將任務dispatch到main queue:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self goDoSomethingLongAndInvolved]; dispatch_async(dispatch_get_main_queue(), ^{ [textField setStringValue:@"Done doing something long and involved"]; }); });
還有一個函數叫dispatch_sync,它乾的事兒和dispatch_async相同,可是它會等待block中的代碼執行完成並返回。結合 __block類型修飾符,能夠用來從執行中的block獲取一個值。例如,你可能有一段代碼在後臺執行,而它須要從界面控制層獲取一個值。那麼你可使用dispatch_sync簡單辦到:
__block NSString *stringValue; dispatch_sync(dispatch_get_main_queue(), ^{ // __block variables aren't automatically retained // so we'd better make sure we have a reference we can keep stringValue = [[textField stringValue] copy]; }); [stringValue autorelease]; // use stringValue in the background now
咱們還可使用更好的方法來完成這件事——使用更「異步」的風格。不一樣於取界面層的值時要阻塞後臺線程,你可使用嵌套的block來停止後臺線程,而後從主線程中獲取值,而後再將後期處理提交至後臺線程:
dispatch_queue_t bgQueue = myQueue; dispatch_async(dispatch_get_main_queue(), ^{ NSString *stringValue = [[[textField stringValue] copy] autorelease]; dispatch_async(bgQueue, ^{ // use stringValue in the background now }); });
取決於你的需求,myQueue能夠是用戶隊列也可使全局隊列。
再也不使用鎖(Lock)
用戶隊列能夠用於替代鎖來完成同步機制。在傳統多線程編程中,你可能有一個對象要被多個線程使用,你須要一個鎖來保護這個對象:
NSLock *lock;
訪問代碼會像這樣:
- (id)something { id localSomething; [lock lock]; localSomething = [[something retain] autorelease]; [lock unlock]; return localSomething; } - (void)setSomething:(id)newSomething { [lock lock]; if(newSomething != something) { [something release]; something = [newSomething retain]; [self updateSomethingCaches]; } [lock unlock]; }
使用GCD,可使用queue來替代:
dispatch_queue_t queue;
要用於同步機制,queue必須是一個用戶隊列,而非全局隊列,因此使用usingdispatch_queue_create初始化一個。而後能夠用dispatch_async 或者 dispatch_sync將共享數據的訪問代碼封裝起來:
- (id)something { __block id localSomething; dispatch_sync(queue, ^{ localSomething = [something retain]; }); return [localSomething autorelease]; } - (void)setSomething:(id)newSomething { dispatch_async(queue, ^{ if(newSomething != something) { [something release]; something = [newSomething retain]; [self updateSomethingCaches]; } }); }
值得注意的是dispatch queue是很是輕量級的,因此你能夠大用特用,就像你之前使用lock同樣。
如今你可能要問:「這樣很好,可是有意思嗎?我就是換了點代碼辦到了同一件事兒。」
實際上,使用GCD途徑有幾個好處:
總結
如今你已經知道了GCD的基本概念、怎樣建立dispatch queue、怎樣提交Job至dispatch queue以及怎樣將隊列用做線程同步。接下來我會向你展現如何使用GCD來編寫平行執行代碼來充分利用多核系統的性能^ ^。我還會討論GCD更深層的東西,包括事件系統和queue targeting。