iOS多線程--GCD

只需知道如何開線程、會看相關的第三方框架便可。markdown

簡介

-

兩個核心概念

任務:執行什麼操做 隊列:用來存聽任務多線程

隊列類型:併發

  • 併發隊列(Concurrent Dispatch Queue)

可讓多個任務併發執行(自動開啓多個線程同時執行任務) 在異步dispatch_async函數下才有效app

  • 串行隊列(Serial Dispatch Queue)

讓任務一個接着一個地執行(一個任務執行完畢後,再執行下一個) 按照FIFO順序執行。框架

任務派發:異步

  • 同步

指阻塞當前線程,既要等待添加的耗時任務塊Block完成後,函數才能返回,後面的代碼才能繼續執行。async

  • 異步

指將任務添加到隊列後,函數當即返回,後面的代碼不用等待添加的任務完成便可立刻執行。異步提交沒法肯定任務執行順序。函數

GCD多線程常常會使用 同步函數dispatch_sync、異步函數dispatch_async,向指定隊列 添加任務。url

分辨

-

各類隊列的執行效果spa

-

主隊列(一種串行隊列)

凡是放到主隊列中的任務,都必須在主線程中執行。

1.異步函數+主隊列

串行執行,全部任務都在主線程中執行,不會開線程

2.同步函數+主隊列————》會死鎖!通常不用。

主隊列的特色:若是主隊列發現當前主線程有任務在執行,那麼它會暫停隊列中的任務,直到主線程空閒爲止。

GCD實現線程間通訊

說明:在更新UI時回到了主線程(經過新設一個同步/異步函數,隊列參數爲主隊列)

  • 開子線程作異步任務
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //...作一些任務
        
        dispatch_sync(dispatch_get_main_queue(), ^{
            //回到主線程,更新UI
        });
    });
複製代碼

舉例,開啓子線程下載圖片

//1.獲取一個 全局串行隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  
   __weak typeof(self) weakSelf = self;
   //2.把任務添加到隊列中執行
    dispatch_async(queue, ^{
        NSURL *url = [NSURL URLWithString:_imgString];
        NSData *imageData = [NSData dataWithContentsOfURL:url];
        UIImage * bgImage = [UIImage imageWithData:imageData];
        bgImage = [bgImage applyLightEffectAtFrame:CGRectMake(0, 0, bgImage.size.width, bgImage.size.height)];
        //3.回到主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            weakSelf.backImageView.image = bgImage;
        });
    });
複製代碼

GCD經常使用函數

  • 延遲執行
int64_t delayInSeconds = 10.0;      // 延遲的時間

dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){           
     // do something  ...  ... 
});

或

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)( 10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // ...     
});

複製代碼
  • 一次性代碼

這個應用程序只執行一次,再也不執行

static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        //...
    });
複製代碼

不能應用於懶加載。應用於單例模式。

  • 柵欄函數

用於控制異步函數的執行順序,只有執行完柵欄函數,才能執行後面的隊列任務。使用時,必須本身建立隊列,不能使用全局併發隊列。

同步函數沒有必要用柵欄函數。

  • 快速迭代(遍歷)

內部會開子線程和主線程一塊兒完成遍歷任務,異步執行任務

  • 主線程異步任務
dispatch_async(dispatch_get_main_queue(), ^{
    
    });
複製代碼
  • 柵欄函數

用於控制異步函數的執行順序。可避免數據競爭。使用時,必須本身建立隊列,不能使用全局併發隊列。

//建立隊列
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
    
    //異步函數
    dispatch_async(queue, ^{
        
        //....異步任務1
    });
    
    dispatch_async(queue, ^{
        
        //....異步任務2
    });
    
    //柵欄函數
    dispatch_barrier_async(queue, ^{
        
        //...
    });
複製代碼

一個dispatch barrier 容許在一個併發隊列中建立一個同步點。當在併發隊列中遇到一個barrier, 他會延遲執行barrier的block,等待全部在barrier以前提交的blocks執行結束。 這時,barrier block本身開始執行。 以後, 隊列繼續正常的執行操做。

  • 隊列組

某幾個隊列的任務都完成後,再執行其餘任務

//建立隊列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    /建立隊列組
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        //...異步任務1
    });
    dispatch_group_async(group, queue, ^{
        //...異步任務2
    });
    dispatch_group_async(group, queue, ^{
        //...異步任務3
    });
    
    //  攔截通知,當隊列組裏的任務都完成了,會進入下面這個方法
    dispatch_group_notify(group, queue, ^{
        //... ... 
    });
複製代碼

如,加載多個圖片

// 使用Dispatch Group追加block到Global Group Queue,這些block若是所有執行完畢,
就會執行Main Dispatch Queue中的結束處理的block。
// 建立隊列組
dispatch_group_t group = dispatch_group_create();
// 獲取全局併發隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ }); 
// 當併發隊列組中的任務執行完畢後纔會執行這裏的代碼
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 合併圖片
});
複製代碼
  • 定時任務
/* 在指定線程上定義計時器 */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    /* 開始的時間 */
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
    /* 設置計時器 ,時間間隔 5s  */
    dispatch_source_set_timer(_timer, when, 5.0 * NSEC_PER_SEC, 0);
    /* 計時器回調block */
    dispatch_source_set_event_handler(_timer, ^{
        // do  something
        NSLog(@"須要作的任務!");
        
    });
    /* 開啓計時器 */
    dispatch_resume(_timer);

    self.timer = _timer;   // 強引用計時器對象(必須)
複製代碼

必須銷燬self.timer才能終止定時任務:self.timer = nil ;

GCD取消線程

能夠取消還未執行的線程。可是沒辦法作到取消一個正在執行的線程。

  • 取消函數 dispatch_block_cancel
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_block_t block1 = dispatch_block_create(0, ^{
    NSLog(@"block1");
});
dispatch_block_t block2 = dispatch_block_create(0, ^{
    NSLog(@"block2");
});
dispatch_block_t block3 = dispatch_block_create(0, ^{
    NSLog(@"block3");
});
    
dispatch_async(queue, block1);
dispatch_async(queue, block2);
dispatch_async(queue, block3);

dispatch_block_cancel(block3);   // 取消 block3
複製代碼

投機取巧:使用臨時變量 + return 方式取消 正在執行的Block。

__block BOOL gcdFlag = NO; // 臨時變量
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    for (long i=0; i<1000; i++) {
        NSLog(@"正在執行第i次:%ld",i);
        sleep(0.1);
        if (gcdFlag==YES) { // 判斷並終止
            NSLog(@"終止");
            return ;
        }
    };
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"我要中止啦");
    gcdFlag = YES;
});
複製代碼
相關文章
相關標籤/搜索