多線程--- GCD

GCD 全稱Grand Central Dispatch,是純C語言,它會自動掛哪裏線程的生命週期。

多線程最基本的運行原理就是將任務添加到隊列,並且指定執行任務的函數,如下:

    //1:創建串行隊列
    dispatch_queue_t queue = dispatch_queue_create("QueueName", DISPATCH_QUEUE_SERIAL);

    //2:創建任務
    dispatch_block_t taskBlock = ^{
        NSLog(@"%@",[NSThread currentThread]);
    };
    //3:利用函數把任務放入隊列
    dispatch_sync(queue, taskBlock);

隊列分爲串行隊列和並行隊列 ,函數分爲 同步函數(dispatch_sync)和異步函數(dispatch_async),隊列和函數類型的組合,決定了能都開闢新的線程

  • 同步:必須等待當前語句執行完畢,纔會執行下一條語句,不會開啓線程。
  • 異步:不用等待當前語句執行完畢就可以執行下一條任務。
  •     a、並行隊列 + 異步函數 開啓多條線程,不按順序執行任務;
  •     b、串行隊列 + 異步函數 開啓一條新線程,按順序執行任務;
  •     c、主隊列 + 異步函數不開啓新線程,按順序執行任務
  •     d、並行隊列 + 同步函數不開啓新線程,按順序執行任務;
  •     e、串行隊列 + 同步函數不開啓新線程,按順序執行任務;
  •     f、主隊列 + 同步函數會出現卡死現象!原因:循環等待,主隊列的東西要等主線程執行完,又不能開線程,所以下面的任務要等上面的任務執行完,然後卡死

關於函數與隊列的組合Demo,點擊此處下載~

還有一種情況的死鎖

執行順序如圖所示, 當執行到sync_block的時候,由於是一個同步函數調用的串行隊列,同步函數必須要先執行它block 裏邊的任務3,但是串行隊列FIFO原則,3必須等到 4執行後才能執行, 雙方相互等待,形成死鎖。

關於主隊列死鎖

    NSLog(@"0");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"1");
    });
    NSLog(@"2");

同步函數調用主隊列(主隊列不是串行隊列但是類似串行隊列),同樣會造成死鎖。

 

柵欄函數

當我們的任務有依賴關係的時候,比如任務1和2執行完畢後才能執行任務3和4,這時候我們可以用到這個函數——柵欄函數。其中 queue 是隊列,block 是任務。

dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t blcok)

提交一個柵欄函數在同步執行中,它會等待柵欄函數執行完再去執行下一行代碼,同步柵欄函數是在主線程中執行的。

dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t blcok)

提交一個柵欄函數在異步執行中,它會立馬返回開始執行下一行代碼(不用等待任務執行完畢)。

 

 前提:  必須是同一個隊列(queue)

注意點: 柵欄函數不能封裝異步網絡請求,因爲異步網絡請求不能保證任務在同一個隊列上。

共同點

  1. 都會等待自己前邊插入的隊列的任務(1、2、3)先執行完。
  2. 都會等待他們自己的任務(barrier)執行完後再執行後的任務。

不同點

  1. dispatch_barrier_sync 需要等待自己的任務(barrier)結束之後,纔會繼續添加並執行寫在barrier後邊的任務,然後執行。
  2. dispatch_barrier_async將自己的任務(barrier)插入到隊列之後,不用等待自己任務的結束,會繼續把後邊的任務添加到隊列中,然後開始執行。
     

調度組

我們經常需要需要監聽多個異步任務全部完成後的回調。此時需要用到調度組處理

注意點: 必須是同一個組group

場景:使用異步下載或者請求的時候,等多個任務都執行完時, dispatch_group_notify()監聽統一的回調。

基本的調度組執行如下:

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_async(group, queue, ^{
        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:4 repeats:NO block:^(NSTimer * _Nonnull timer) {
        }];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop]run];
    });
    dispatch_group_async(group, queue, ^{
       NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer * _Nonnull timer) {
        }];
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        [[NSRunLoop currentRunLoop]run];
    });
    dispatch_group_wait(group, 5);  // 最長等待時間
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"--5^^ %@",[NSThread currentThread]); // <= 5秒調用
        
    });

調度組內部方法 enter - leave

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"--1^^ 執行完了,準備出組");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"--2^^ 執行完了,準備出組");
        dispatch_group_leave(group);
    });
    
    dispatch_group_wait(group, 5);
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"--5^^ %@",[NSThread currentThread]);
    });

 

dispatch_group_enter() 與 dispatch_group_leave  保證成對出現,

dispatch_group_leave 少於dispatch_group_enter(),將不會調用通知, 反之crash;

 

信號量

信號量是用在多線程併發的可控最大任務數量的操作,一個線程完成了某一個動作就通過信號量告訴別的線程,別的線程再進行某些動作

// 創建一個信號,value:信號量
dispatch_semaphore_create(<#long value#>)
// 使某個信號的信號量+1
dispatch_semaphore_signal(<#dispatch_semaphore_t dsema#>)
// 某個信號進行等待或等待降低信號量 timeout:等待時間,永遠等待爲 DISPATCH_TIME_FOREVER
dispatch_semaphore_wait(<#dispatch_semaphore_t dsema#>, <#dispatch_time_t timeout#>)
正常的使用順序是先降低然後再提高,這兩個函數通常成對使用。
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);  // 同時執行任務的數量,不超過2個

      dispatch_async(queue, ^{
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          NSLog(@"執行任務1");
          sleep(1);
          NSLog(@"任務1完成");
          dispatch_semaphore_signal(semaphore);
      });
      
      dispatch_async(queue, ^{
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          NSLog(@"執行任務2");
          sleep(1);
          NSLog(@"任務2完成");
          dispatch_semaphore_signal(semaphore);
      });
    
      dispatch_async(queue, ^{
          dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
          NSLog(@"執行任務3");
          sleep(1);
          NSLog(@"任務3完成");
          dispatch_semaphore_signal(semaphore);
      });
      

dispatch_source

關於source源的設置, 1、2、3 爲創建, 4、5、6爲設置。

dispatch_source_create

1、創建source源

dispatch_source_set_event_handler

2、設置回調

dispatch_source_merge_data

3、引發回調(綁定source, 帶參data)

dispatch_source_get_data

4、獲取source綁定的data

dispatch_suspend

5、掛起

dispatch_resume

6.繼續

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章