GCD Tips

GCD

Grand Central Dispatch(GCD)是Apple開發的一個多核編程的解決方法。該方法在MacOSX10.6雪豹中首次推出,並隨後被引入到了iOS4.0中。GCD是一個替代諸如NSThread, NSOperationQueue等技術的很高效和強大的技術。GCD可以幫助咱們使用很是簡潔高效的方法實現複雜繁瑣的多線程編程。html

下面的例子列舉了簡單的異步線程處理,在後臺處理完耗時的程序以後,在主線程更新UI。編程

dispatch_async(queue, ^{
       /*
        * ...
        * 長時間處理的Code
        */
        
        //處理結束,主線程更新UI
        // 使用dispatch_get_main_queue() 得到主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            // ... code
            // 執行須要在主線程執行的更新UI代碼
        });
    });
複製代碼

GCD的實現原理

簡單來講,GCD的實現須要使用這些工具:bash

  • 用於管理追加的Block的C語言實現的FIFO隊列
  • Atomic函數中實現的額用於排他控制的輕量級信號
  • 用於管理線程的C語言層實現的一些容器

一般,應用程序中編寫的線程管理應用的代碼要在系統iOS和OS X的核心XNU內核級上實現。所以,不管編程人員如何努力編寫管理線程的代碼,在性能方面也不可能賽過XNU內核級所實現的GCD。網絡

使用GCD要比使用pthreads和NSThread這些通常的多線程編程API更好。而且,若是使用GCD就沒必要編寫爲操做線程反覆出現的相似的代碼(這被稱爲固定源代碼片斷),而能夠在線程中集中實現處理內容。咱們儘可能多使用GCD或者使用了Cocoa框架GCD的NSOperationQueue類等API。多線程

用於實現Dispatch Queue而使用的軟件組件。app

組件名稱 提供技術
libdispatch Dispatch Queue
Libc(pthreads) pthread_workqueue
XNU內核 workqueue

編程人員所使用GCD的API所有包含在libdispatch庫中的C語言函數。Dispatch Queue經過結構體和鏈表,被實現爲FIFO隊列。FIFO隊列管理是經過dispatch_async函數所追加的Block。框架

Block並非直接加入FIFO隊列,而是先加入Dispatch Continuation這一==dispatch_continuation_t==類型結構體中,而後再加入FIFO隊列。該Dispatch Continuation用於記憶Block所屬的Dispatch Group和其餘一些信息,至關於通常常說的執行上下文。異步

Dispatch Queue可經過==dispatch_set_target_queue==函數設定,能夠設定執行該Dispatch Queue處理的Dispatch Queue爲目標。該目標可像串珠子同樣,設定多個鏈接在一塊兒的Dispatch Queue。可是在鏈接串的最後必須設定爲Main Dispatch Queue,或各類優先級的Global Dispatch Queue,或是準備用於Serial Dispatch Queue的各類優先級的Global Dispatch Queue。async

Main Dispatch Queue在RunLoop 中執行Block。ide

Global Dispatch Queue有以下8中:

Global Dispatch Queue(High Priority)
Global Dispatch Queue(Default Priority)
Global Dispatch Queue(Low Priority)
Global Dispatch Queue(Background Priority)
Global Dispatch Queue(High Overcommit Priority)
Global Dispatch Queue(Default Overcommit Priority)
Global Dispatch Queue(Low Overcommit Priority)
Global Dispatch Queue(Background Overcommit Priority)
複製代碼

優先級中附有Overcommit的Global Dispatch Queue使用在Serial Dispatch Queue中。如Overcommit 這個名稱所示,無論系統狀態如何,都會強制生成線程的Dispatch Queue。 這8種Global Dispatch Queue各使用1個pthread_workqueue。GCD初始化時,使用pthread_workqueue_create_np函數生成pthread_workqueue。

pthread_workqueue包含在Libc提供的pthreads API中。其使用bsdthread_register和workq_open系統調用,在初始化XNU內核的workqueue以後獲取workqueue信息。

XNU內核持有4中workqueue:

WORKQUEUE_HIGH_PRIORITY
WORKQUEUE_DEFAULT_PRIORITY
WORKQUEUE_LOW_PRIORITY
WORKQUEUE_BG_PRIORITY
複製代碼

以上爲4中執行優先級的workqueue。該執行優先級與Global Dispatch Queue的4種執行優先級相同。

下面看一下Dispatch Queue中執行Block的過程。當在Global Dispatch Queue中執行Block時,libdispatch從Global Dispatch Queue自身的FIFO隊列中提出Dispatch Continuation,調用pthread_workqueue_additem_np函數。將該Global Dispatch Queue自身、符合其優先級的workqueue信息以及爲執行Dispatch Continuation的回調函數等傳遞給參數。

該線程雖然與iOS和OS X中一般使用的線程大體相同,可是有一部分pthread API不能使用。詳細信息科參考蘋果的官方文檔《並列編程指南》的「與POSIX線程的互換性」一節。

另外,由於workqueue生成的線程在實現用於workqueue的線程計劃表中運行,因此與通常線程的上下文切換不一樣。這裏也隱藏着使用GCD的緣由。

Block執行結束後,進行通知Dispatch Group結束、釋放Dispatch Continuation等處理,開始準備執行加入到Globar Dispatch Queue的下一個Block。


Dispatch Source

GCD中除了主要的Dispatch Queue外,還有不太引人注目的Dispatch Source。它是BSD系內核慣有功能kqueue的包裝。

kqueue是XNU內核中發生各類事件時,在應用程序編程方執行處理的技術。其CPU負荷很是小,儘可能不佔用資源。kqueue能夠說是應用程序處理XNU內核中發生的各類事件的方法中最優秀的一種。

Dispatch Source可處理如下事件:

DISPATCH_SOURCE_TYPE_DATA_ADD	變量增長
DISPATCH_SOURCE_TYPE_DATA_OR	變量OR
DISPATCH_SOURCE_TYPE_MACH_SEND	MACH端口發送
DISPATCH_SOURCE_TYPE_MACH_RECV	MACH端口接收
DISPATCH_SOURCE_TYPE_PROC	檢測到與進程相關的事件
DISPATCH_SOURCE_TYPE_READ	可讀取文件映像
DISPATCH_SOURCE_TYPE_SIGNAL	接收信號
DISPATCH_SOURCE_TYPE_TIMER	定時器
DISPATCH_SOURCE_TYPE_VNODE	文件系統有變動
DISPATCH_SOURCE_TYPE_WRITE	可寫入文件映像
複製代碼

事件發生時,在指定的Dispatch Queue中可執行事件的處理。 下面咱們使用DISPATCH_SOURCE_TYPE_READ,異步讀取文件映像。

*
    __block size_t total = 0;
    size_t size = 1024 * 10;//要讀取的字節數
    char * buff = (char *)malloc(size);

    /*
     *  設定爲異步映像
     */
    fcntl(sockfd, F_SETFL, O_NONBLOCK);

    /*
     *  獲取用於追加事件處理的Global Dispatch Queue
     */    
    dispatch_queue_t queue = dispatc_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    /*
     *  基於READ事件做成Dispatch Source
     */  
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0 , queuq);

    /*
     *  指定發生READ事件時執行的處理
     */    
    dispatch_source_set_event_handler(source, ^{
          /*
           *  獲取可讀取的字節數
           */
          size_t available = dispatch_source_get_data(source);
          /*
           *  從映像中讀取
           */
          int length = read(sockfd, buff, available);
          /*
           *  發生錯誤時取消Dispatch Source
           */
          if (length < 0)
          {
                // 錯誤處理
                dispatch_source_cancel(source);
          }

          total += length;

          if (total == size)
          {
                // buff 的處理

                // 處理結束,取消Dispatch Source
                dispatch_source_cancel(source);
          }
    });

    /*
     *  指定取消Dispatch Source時的處理
     */
    dispatch_source_set_cancel_handler(source, ^{
        free(buff);
        close(sockfd);

        /*
         *  釋放Dispatch Source(自身)
         */
        dispatch_release(source);
    });

    /*
     *  啓動Dispatch Source
     */

    dispatch_resume(source);
複製代碼

與上面代碼很是類似的代碼,使用在了Core Foundation框架的用於異步網絡的API CFSocket中。由於Foundation框架的異步網絡API是經過CFSocket實現的,因此可享受到僅使用Foundation框架的Dispatch Source(即GCD)帶來的好處。

參考文檔

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息