iOS多線程-02-GCD

簡介


  • GCD(Grand Center Dispatch)是Apple爲多核的並行運算提出的解決方案,純C語言
  • 更加適配多核處理器,且自動管理線程的生命週期,使用起來較爲方便
  • GCD經過任務和隊列實現多線程功能
    • 任務:描述所要執行的操做
    • 隊列:用來存放所要執行的任務,隊列中的任務遵循FIFO(First In First Out)原則

GCD的任務函數(是否開啓新的線程)


  • 同步
    • 不具有開啓新的線程的能力
    • 同步執行任務的函數
      • void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block),Block類型
        • queue:任務隊列
        • block(代碼塊):所執行的任務
      • void dispatch_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work),函數類型(每一個Block類型都對應一個函數類型)
        • queue:任務隊列
        • context:傳遞給任務函數的參數
        • work(函數):所執行的任務
    • 同步執行任務的其餘函數(barrier),在前面的任務執行完畢它才執行,它後邊的任務等它執行完畢才執行
      • void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block),block類型
        • queue:任務隊列,僅當該參數爲併發隊列時,該函數纔有意義
      • dispatch_barrier_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work),函數類型
  • 異步
    • 具有開啓新的線程的能力(須要將任務添加到併發隊列中)
    • 異步執行任務的函數(參數意義與同步函數相同)
      • void dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
      • void dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work)
    • 異步執行任務的其餘函數(barrier),在前面的任務執行完畢它才執行,它後邊的任務等它執行完畢才執行(參數意義與同步函數相同)
      • void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block)
      • void dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work)

GCD的隊列(任務的執行方式)


  • 併發隊列
    • 開啓多個線程,使隊列中的多個任務併發執行(須要異步執行函數的配合)
  • 串行隊列
    • 隊列中的任務一個接一個順序地執行
  • 隊列的種類
    • 串行隊列
      • dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
        • label:一般爲0
        • attr:隊列類型,DISPATCH_QUEUE_SERIAL
    • 併發隊列
      • dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
        • label:一般爲0
        • attr:隊列類型 DISPATCH_QUEUE_CONCURRENT
    • 主隊列(串行,只能在主線程中運行)
      • dispatch_queue_t dispatch_get_main_queue(void),獲取主隊列
    • 全局隊列(併發)
      • dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags)

任務與隊列的組合


  • 同步函數
    • 同步函數主隊列多線程

      /**
      - 運行在主線程主隊列(未開啓新的線程),主線程被卡死
      - 緣由:任務代碼等待着當前函數執行完畢才能執行(當前函數正在執行且未執行完畢);
             當前函數等待着任務代碼 執行完畢才能執行(當前任務正在執行且未執行完畢);
             相互等待,出現死鎖
      */
      
      //獲取主隊列
      dispatch_queue_t queue = dispatch_get_main_queue();
      //添加任務到隊列
      dispatch_sync(queue, ^{
          //任務1代碼
      });
      dispatch_sync(queue, ^{
          //任務2代碼
      });
    • 同步函數串行隊列併發

      /**
      - 運行在主線程串行非主隊列(未開啓新的線程),任務串行執行
      */
      
      //建立串行隊列
      dispatch_queue_t queue = dispatch_queue_create("com.23565@qq", DISPATCH_QUEUE_SERIAL);
      //添加任務到隊列
      dispatch_sync(queue, ^{
          //任務1代碼
      });
      dispatch_sync(queue, ^{
          //任務2代碼
      });
    • 同步函數併發隊列app

      /**
      - 運行在非主線程併發隊列(未開啓新的線程),任務串行執行
      */
      
      //獲取全局隊列(併發)
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      //將任務加至隊列
      dispatch_sync(queue, ^{
          //任務1代碼
      });
      dispatch_sync(queue, ^{
          //任務2代碼
      });
  • 異步函數
    • 異步函數主隊列異步

      /**
      - 運行在主線程主隊列(未開啓新的線程),任務串行執行
      */
      
      // 得到主隊列
      dispatch_queue_t queue = dispatch_get_main_queue();
      // 將任務加入隊列
      dispatch_async(queue, ^{
          //任務1代碼
      });
      dispatch_async(queue, ^{
          //任務2代碼
      });
    • 異步函數串行隊列async

      /**
      - 運行在主函數串行非主隊列(未開啓新的線程),任務串行執行
      */
      
      //建立串行隊列
      dispatch_queue_t queue = dispatch_queue_create("com.23565@qq", DISPATCH_QUEUE_SERIAL);
      //將任務加至隊列
      dispatch_async(queue, ^{
          //任務1代碼
      })
      dispatch_async(queue, ^{
          //任務2代碼
      })
    • 異步函數併發隊列ide

      /**
      - 運行在非主線程併發隊列(開啓新的線程),任務併發執行
      - 系統根據任務建立線程(沒法肯定任務執行在哪一個線程)
      */
      
      //得到全局併發隊列
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      //將任務加入隊列
      dispatch_async(queue, ^{
          //任務1代碼
      });
      dispatch_async(queue, ^{
          //任務2代碼
      });

      線程之間的通訊


  • 從主線程到子線程
    • 注意
      • 只有異步函數與併發隊列的組合,纔會開啓新的線程,使任務併發執行
      • 一般使用異步函數將任務添加到併發隊列中,來實現從主線程到子線程的通訊
    • 實現代碼函數

      dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
              //須要在子線程中執行的任務代碼
          })
  • 從子線程到主線程
    • 注意
      • 主隊列中的任務只能在主線程中執行
      • 一般使用異步/同步函數將任務添加到主隊列中,來實現從子線程到主線程的通訊
    • 實現代碼ui

      dispatch_async(dispatch_get_main_queue(), ^{
              //須要在主線程中執行的代碼
          })

GCD的其餘任務


  • 單次執行(一般用在單例模式的設計中)spa

    //定義一個標記
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //此處的代碼只會被執行一次
    });
  • 延遲執行線程

    /**
    - 方法一(GCD)
    */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //此處的代碼將延遲執行
    });
    
    /**
    - 方法二(performSelector)
    */
    [self performSelector:@selector(run) withObject:self afterDelay:2.0];
    
    /**
    - 方法三(NSTimer)
    */
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO]
  • 快速迭代

    void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t))
    /**
        iterations:迭代執行的次數
        queue:任務隊列
        block:迭代執行的代碼
        size_t:用來定義當前迭代到第幾回,須要本身添加,如在size_t後添加index索引,記錄當前的迭代次數
    */
  • Barrier

    /**
    - Barrier中的任務,只能在它前面的任務執行完畢才能執行
      Barrier後的任務,只能等到它執行完畢才能執行
    - 要將隊列添加到本身建立的併發隊列中,否其功能等同於函數
      void dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
    */
    //建立隊列(一般是本身建立的併發隊列)
    dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_CONCURRENT);
    //將任務添加到隊列
    dispatch_async(queue, ^{
        //在Barrier前執行的任務代碼
    });
    dispatch_barrier_async(queue, ^{
       //Barrier中的任務代碼
    });
    dispatch_async(queue, ^{
        //在Barrier後執行的任務代碼
    });
  • 隊列組

    //獲取全局併發隊列
    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, ^{
            //任務1代碼
        });
    dispatch_group_async(group, queue, ^{
            //任務2代碼
        });
    dispatch_group_notify(group, queue, ^{
            //任務3代碼
            /**
            group組中的全部任務執行完畢在執行
            若group爲空,則當即執行
            */
    });

GCD定時器


  • 實現原理
    • 建立一個DISPATCH_SOURCE_TYPE_TIMER類型的dispatch source,並添加到dispatch queue,經過dispatch source來響應事件
    • 經過函數void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway),來設置dispatch source的執行事件
  • 實現代碼

    //得到隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    //建立一個定時器
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    //設置定時器的各類屬性(起止時間)
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(8.0 * NSEC_PER_SEC));
    uint64_t interval = (uint64_t)(1.0 * NSEC_PER_SEC);
    //設置
    dispatch_source_set_timer(self.timer, start, interval, 0);
    
    //設置回調
    dispatch_source_set_event_handler(self.timer, ^{
        //定時器被觸發時所要執行的代碼
    });
    
    //開啓定時器
    dispatch_resume(self.timer);
    //取消定時器
    dispatch_cancel(self.timer);
相關文章
相關標籤/搜索