GCD筆記

  蘋果官方說明,GCD是異步執行任務的技術之一,通常將應用程序中記述的線程管理用的代碼在系統級中實現。開發者只須要定義想執行的任務並追加到適當的Dispatch Queue中,GCD就能生成必要的線程並計劃執行任務。因爲線程管理是做爲系統的一部分來實現的,所以能夠統一管理,也能夠執行任務。以下:數據庫

 
 

    //串形隊列 第二個參數最好爲 NULL 參考源碼編程

  dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.mySerialQueue", NULL);

     //並行隊列 第二個參數最好爲 DISPATCH_QUEUE_CONCURRENT數組

    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.gcd.myCurrentQueue", DISPATCH_QUEUE_CONCURRENT);安全

  dispatch_async(dispatch_get_global_queue(0, 0), ^{ // 長時間處理 如:數據庫訪問
 dispatch_async(dispatch_get_main_queue(), ^{ //主線程更新
 }); });

其實只有第一行代碼表示讓處理在後臺執行。也只有一行代碼讓處理在主線程中執行。多線程

在GCD以前,Cocoa框架也提供NSObject類來實現一樣的效果:併發

    //執行後臺線程
    [self performSelectorInBackground:@selector(doWork) withObject:nil];
- (void)doWork{
    // 長時間處理  如:數據庫訪問
    // ...........................
    //處理結束  ,主線城處理結果
    [self performSelectorOnMainThread:@selector(doneWork) withObject:nil waitUntilDone:NO];
}

- (void)doneWork{
    //主線程執行    如用戶界面更新
}

這個方法的確要比NSThread提供的簡單,可是相比於GCD仍是繁瑣了。在多線程編程中,因爲CPU一次只能執行一個命令,GCD的優點就更加明顯。app

 

------Dispatch Queue的種類框架

    //獲取主線程 屬於serial queue
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //高優先級獲取方法 屬於concurrent queue
    dispatch_queue_t globalDispatchQueueHigh = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    //默認先級獲取方法 屬於concurrent queue
    dispatch_queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //低優先級獲取方法 屬於concurrent queue
    dispatch_queue_t globalDispatchQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    //後臺優先級獲取方法 屬於concurrent queue
    dispatch_queue_t globalDispatchQueueBg = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);--

-----指定優先級 ,若是在多個serial dispatch queue中用此函數制定目標爲某一個serial dispatch queue,那麼本來能夠併發執行的多個serial dispatch queue,在目標serial dispatch queue上只能同時執行一個處理。異步

   //指定變動優先級 第一個參數若是是系統提供的如main dispatch queue 和  global dispatch queue 均不可指定
    //第一個參數指定變動優先級   第二個參數指定與要使用的執行的相同優先級
    dispatch_set_target_queue(globalDispatchQueueHigh, globalDispatchQueueLow);

-----Dispatch Groupasync

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t groupQueue = dispatch_group_create();
    //這五個block併發執行
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block0");});
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block1");});
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block2");});
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block3");});
    dispatch_group_async(groupQueue, queue, ^{NSLog(@"block4");});
    //結束處理 必定是最後執行的
    dispatch_group_notify(groupQueue, queue, ^{NSLog(@"done");});

dispatch_group中也可使用dispatch_wait函數等待所有處理執行結束

// DISPATCH_TIME_FOREVER 永久等待,group未結束就會一直等待
    dispatch_group_wait(groupQueue, DISPATCH_TIME_FOREVER);
    //如:
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
    long result =  dispatch_group_wait(groupQueue, time);
    if (result == 0) { //若是不爲0,就說明雖然過了等待時間,可是某一個group還在處理執行中。
        //屬於dispatch group的所有處理執行結束
        NSLog(@"屬於dispatch group的所有處理執行結束");
    }else{
        //屬於dispatch group的某一個處理還在執行中
        NSLog(@"屬於dispatch group的某一個處理還在執行中"); }

還有常常碰到的等待多個異步請求執行完畢方法以下:

    dispatch_group_t serviceGroup = dispatch_group_create();
    //開始第一個請求
    //進入組
    dispatch_group_enter(serviceGroup);
    [self.configService startWithCompletion:^(ConfigResponse *resul ts, NSError* error){
        configError = error;
        //離開組
        dispatch_group_leave(serviceGroup);
    }];
    //開始第二個請求
    //進入組
    dispatch_group_enter(serviceGroup);
    [self.preferenceService startWithCompletion:^(PreferenceRespons e *results, NSError* error){
    
        //離開組
        preferenceError = error;
        dispatch_group_leave(serviceGroup);
    }];
    //當小組裏的任務都清空之後 通知主線程
    dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^{
        // Assess any errors
        NSError *overallError = nil;
        if (configError || preferenceError) {
            // Either make a new error or assign one of them to the
            overall error
            overallError = configError ?: preferenceError;
        }
        // Now call the final completion block
        completion(overallError);
    });

 

 ----dispatch_barrier_async在訪問數據庫或文件時,使用serial dispatch queue 可避免數據競爭。

    dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.barrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{NSLog(@"block0_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block1_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block2_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block3_for_reading");});
    //須要寫入處理 -- 當前面執行完再執行dispatch_barrier中的處理,在執行後面的操做
    dispatch_barrier_async(queue, ^{NSLog(@"blockBarrier_for_writing");});
    dispatch_async(queue, ^{NSLog(@"block4_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block5_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block6_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block7_for_reading");});
    dispatch_async(queue, ^{NSLog(@"block8_for_reading");});

         

----dispatch_apply    是dispatch_async和dispatch_group的關聯API。

按指定的次數將指定的block追加到指定的dispatch_queue中,並等待所有執行結束。

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%zu",index);
    });
    NSLog(@"done");

          

有這樣的功能,咱們就能夠很安全的去循環數組進行元素處理(注:不是按順序執行)。

    NSArray *array = @[@"00",@"11",@"22",@"33",@"44",@"55",@"66",@"77",@"88",@"99",@"xx"];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(array.count, queue, ^(size_t index) {
        NSLog(@"%zu-%@",index,array[index]);
    });
    NSLog(@"done");

另外,因爲dispatch_apply 和 diapatch_sync 函數相同。會等待處理執行結束,所以推薦在dispatch_async函數中非同步的執行dispatch_apply函數

    NSArray *array = @[@"00",@"11",@"22",@"33",@"44",@"55",@"66",@"77",@"88",@"99",@"xx"];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //在global dispatch queue中非同步執行
    dispatch_async(queue, ^{
       //等待dispatch_apply函數中所有處理執行結束
        dispatch_apply(array.count, queue, ^(size_t index) {
           //並列處理包含在NSArray對象的所有對象
            NSLog(@"%zu-%@",index,[array objectAtIndex:index]);
        });
        
        //dispatch_apply函數所有處理執行結束
        dispatch_async(dispatch_get_main_queue(), ^{
           //主線程中更新UI
            NSLog(@"主線程中更新UI");
        });
    });

----dispatch_suspend 掛起中止執行, dispatch_resume恢復執行

----dispatch_semaphore 是持有計數的信號,此計數是變線程編程中計數類型信號。相似於過馬路的手旗,能夠經過舉起手旗,不可經過放下手旗。dispatch_semaphore,計數爲0等待,計數大於或等於1時,減去1而不等待。

//建立dispatch_semaphore 計數值初始化爲1
    dispatch_semaphore_t  semaphore = dispatch_semaphore_create(1);
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
    long result = dispatch_semaphore_wait(semaphore, time);
    if (result == 0) {//返回爲0的時候能夠安全的執行能夠進行排他控制的處理
        /*
         因爲dispatch_semaphore的計數值達到大於等於1
         或者在待機中的指定時間內
         dispatch_semaphore的計數值達到大於等於1
         因此dispatch_semaphore的計數值減去1.
         
         可執行須要進行排他控制處理
         */
    }else{
        /*
         因爲dispatch semaphore的計數值爲0
         所以在達到指定時間爲止待機
         */
    }

實際使用:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //建立dispatch_semaphore 計數值初始化爲1
    //保證訪問NSMutableArray類對象的線程同時只能有一個
    dispatch_semaphore_t  semaphore = dispatch_semaphore_create(1);
    NSMutableArray *array = [[NSMutableArray alloc]init];
    
    for (int i = 0 ; i < 10000; i ++) {
        dispatch_async(queue, ^{
           /*
            等待dispatch_smaphore
            一直等待,直到dispatch_semaphore的計數值達到大於或等於1
            */
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            /*
             因爲dispatch_semaphore的計數值達到大於等於1
             因此將dispatch_semaphore的計數值減去1
             dispatch_semaphore_wait函數執行返回
             
             即此時的dispatch semaphore 恆爲0
             
             因爲能夠訪問NSMutableArray類對象的線程只有1個
             所以能夠安全的進行更新
             */
            [array addObject:[NSNumber numberWithInt:i]];
            /*
             排他控制處理結束,因此經過dispatch_semaphore_signal函數將dispatch_samaphore的計數值加1.
             
             若是有經過dispatch_semaphore_wait函數等待dispatch_semaphore的計數值增長的線程,就由最早等待的線程執行。
             */
            dispatch_semaphore_signal(semaphore);
        });
    }

在沒有serial dispatch queue和dispatch_barrier_async函數那麼大粒度且一部分處理須要進行排他處理的狀況下,diapatch_semaphore即可以發揮威力。

----Dispatch I/O  

在讀取較大文件時,若是將文件分紅合適的大小並使用global dispatch queue並列讀取的話,會比通常的讀取快很多。如今的輸入/輸出硬件已經能夠作到一次使用多個線程更快的並列讀取了。能實現這個功能的只有Dispatch I/O 和 Dispatch Data,這個後面更新。

相關文章
相關標籤/搜索