iOS 多線程之GCD

級別: ★★☆☆☆
標籤:「iOS」「多線程」「GCD」
做者: dac_1033
審校: QiShare團隊php

1、GCD簡介

Grand Central Dispatch(GCD)是由蘋果開發的多線程調度框架,可以優化多線程應用程序的執行過程以支持多核處理器。GCD底層有線程池,線程池的概念在上一篇文章中有簡單介紹,線程池是系統自動來維護,開發者只關注隊列(Dispatch Queue)及任務的建立和同步異步調用便可。 iOS中使用GCD來實現線程操做很簡單:建立隊列 > 同步/異步調用 > 將任務放在上一步調用的block中。下面咱們來實現異步執行耗時操做,當耗時操做執行完畢時,回到主線程來更新相應的UI的功能,簡易的代碼以下:git

dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
      // 放一些極其耗時間的任務在此執行
      dispatch_async(dispatch_get_main_queue(), ^{
          // 耗時任務完成,拿到資源,更新UI更新UI只能夠在主線程中更新
      });
});
複製代碼

在調用GCD中方法執行多線程操做時,GCD可自動利用CPU的多核實現異步處理任務,自動管理線程的生命週期(建立線程、調度任務、銷燬線程),開發者只須要設置GCD須要執行的任務,不用編寫任何線程管理代碼。所以GCD也是蘋果最爲推薦開發者使用的多線程框架。github

2、GCD詳解

首先咱們使用GCD實現異步加載多張網絡圖片,來熟悉一下GCD的使用過程:bash

//// 自定義圖片Model
@interface GCDImage : NSObject

@property (nonatomic, assign) NSInteger index;
@property (nonatomic, strong) NSData *imgData;

@end
@implementation GCDImage

@end


////  GCD加載一張網絡圖片
#define ColumnCount 4
#define RowCount 5
#define Margin 10

@interface MultiThread_GCD ()

@property (nonatomic, strong) NSMutableArray *imageViews;

@end

@implementation MultiThread_GCD

- (void)viewDidLoad {
    
    [super viewDidLoad];
    [self setTitle:@"GCD"];
    [self.view setBackgroundColor:[UIColor whiteColor]];
    self.edgesForExtendedLayout = UIRectEdgeNone;
    
    [self layoutViews];
}

- (void)layoutViews {
    
    CGSize size = self.view.frame.size;
    CGFloat imgWidth = (size.width - Margin * (ColumnCount + 1)) / ColumnCount;
    
    _imageViews=[NSMutableArray array];
    for (int row=0; row<RowCount; row++) {
        for (int colomn=0; colomn<ColumnCount; colomn++) {
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(Margin + colomn * (imgWidth + Margin), Margin + row * (imgWidth + Margin), imgWidth, imgWidth)];
            imageView.backgroundColor = [UIColor cyanColor];
            [self.view addSubview:imageView];
            [_imageViews addObject:imageView];
        }
    }
    
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.frame = CGRectMake(15, (imgWidth + Margin) * RowCount + Margin, size.width - 15 * 2, 45);
    [button addTarget:self action:@selector(loadImageWithMultiOperation) forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"點擊加載" forState:UIControlStateNormal];
    [self.view addSubview:button];
}


#pragma mark - 多線程下載圖片

//- (void)loadImageWithMultiOperation {
//
//    int count = RowCount * ColumnCount;
//
//    // 建立一個串行隊列 第一個參數:隊列名稱 第二個參數:隊列類型
//    // 注意queue對象不是指針類型
//    dispatch_queue_t serialQueue=dispatch_queue_create("QiShareSerialQueue", DISPATCH_QUEUE_SERIAL);
//    // 建立多個線程用於填充圖片
//    for (int i=0; i<count; ++i) {
//        //異步執行隊列任務
//        dispatch_async(serialQueue, ^{
//            GCDImage *gcdImg = [[GCDImage alloc] init];
//            gcdImg.index = i;
//            [self loadImg:gcdImg];
//        });
//
//    }
//}

- (void)loadImageWithMultiOperation {
    
    int count = RowCount * ColumnCount;
    
    // 取得全局隊列 第一個參數:線程優先級 第二個參數:標記參數,目前沒有用,通常傳入0
    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 建立多個線程用於填充圖片
    for (int i=0; i<count; ++i) {
        //異步執行隊列任務
        dispatch_sync(globalQueue, ^{
            GCDImage *gcdImg = [[GCDImage alloc] init];
            gcdImg.index = i;
            [self loadImg:gcdImg];
        });
        
    }
}


#pragma mark - 加載圖片

- (void)loadImg:(GCDImage *)gcdImg {
    
    // 請求數據
    gcdImg.imgData = [self requestData];
    
    // 更新UI界面(mainQueue是UI主線程
//    dispatch_sync(dispatch_get_main_queue(), ^{
        [self updateImage:gcdImg];
//    });
    
    // 打印當前線程
    NSLog(@"current thread: %@", [NSThread currentThread]);
}


#pragma mark - 請求圖片數據

- (NSData *)requestData {
    
    NSURL *url = [NSURL URLWithString:@"https://store.storeimages.cdn-apple.com/8756/as-images.apple.com/is/image/AppleInc/aos/published/images/a/pp/apple/products/apple-products-section1-one-holiday-201811?wid=2560&hei=1046&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1540576114151"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    return data;
}


#pragma mark - 將圖片顯示到界面

- (void)updateImage:(GCDImage *)gcdImg {
    
    UIImage *image = [UIImage imageWithData:gcdImg.imgData];
    UIImageView *imageView = _imageViews[gcdImg.index];
    imageView.image = image;
}


@end
複製代碼

下面咱們就來逐步詳細介紹GCD相關的細節...微信

2.1 GCD中用來執行任務的經常使用方法
// 同步執行任務(在當前線程中執行任務,不會開啓新線程)
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

// 異步執行任務(能夠在新線程中執行任務,有開啓新線程的能力)
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
複製代碼

其中,第一個參數爲任務所要加入的隊列;第二個參數是一個dispatch_block_t類型的block,即任務(一段代碼)。網絡

2.2 GCD中的隊列
// 串行隊列的建立方法
dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_SERIAL);
// 併發隊列的建立方法
dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
複製代碼

其中,第一個參數爲隊列的名稱;第二個參數根據取值命名,就可看出爲隊列的類型。多線程

兩個特殊的隊列:併發

  • 主隊列(Main Dispatch Queue):是串行隊列,全部放在主隊列中的任務,都會放到主線程中執行; 獲取主隊列:dispatch_queue_t queue = dispatch_get_main_queue();
  • 全局隊列(Global Dispatch Queue):是併發行隊列; 獲取全局隊列:dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 其中,第一個參數爲隊列優先級,通常用DISPATCH_QUEUE_PRIORITY_DEFAULT;第二個參數用0便可。

則調度方法與任務所在隊列的組合以下:app

調度方法 併發隊列 串行隊列 主隊列
異步(dispatch_async) 開啓多個新線程,併發執行任務 開啓1個新線程,串行執行任務 沒有開啓新線程,串行執行任務
同步(dispatch_sync) 沒有開啓新線程,串行執行任務 沒有開啓新線程,串行執行任務 主線程調用:死鎖致使程序卡死
其餘線程調用:沒有開啓新線程,串行執行任務

咱們能夠運行下面的代碼來驗證上列表組合中每一項的正確性,以下:框架

- (void)addTask:(NSInteger)tag {
    
    [NSThread sleepForTimeInterval:2];
    
    NSLog(@"addTask%ld--->> %@", (long)tag, [NSThread currentThread]);
    
    NSURLSessionTask *task = [NSURLSession.sharedSession dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.so.com/"]] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        
        NSLog(@"任務完成%ld--->> %@", (long)tag, [NSThread currentThread]);
        
    }];
    [task resume];
}


#pragma mark - 串行/併發隊列 + 同步/異步調用組合

// 異步執行
- (void)asyncExecute {
    
    NSLog(@"CurrentThread---%@", [NSThread currentThread]);
    NSLog(@"asyncExecute start");
    
    // + 併發隊列(開啓多個線程,併發執行任務)
    dispatch_queue_t queue = dispatch_queue_create(QiShareQueue, DISPATCH_QUEUE_CONCURRENT);
//    // + 串行隊列(開啓1個新線程,串行執行任務)
//    dispatch_queue_t queue = dispatch_queue_create(QiShareQueue, DISPATCH_QUEUE_SERIAL);
//    // + 主隊列(沒有開啓新線程,串行執行任務)
//    dispatch_queue_t queue = dispatch_get_main_queue();
    
    for (NSInteger i=0; i<10; i++) {
        
        dispatch_async(queue, ^{
            // 追加任務
            [self addTask:i];
        });
    }
    
    NSLog(@"asyncExecute end");
}

// 同步執行
- (void)syncExecute {
    
    NSLog(@"currentThread---%@", [NSThread currentThread]);
    NSLog(@"syncExecute start");
    
//    // + 併發隊列(沒有開啓新線程,串行執行任務)
//    dispatch_queue_t queue = dispatch_queue_create(QiShareQueue, DISPATCH_QUEUE_CONCURRENT);
//    // + 串行隊列(沒有開啓新線程,串行執行任務)
//    dispatch_queue_t queue = dispatch_queue_create(QiShareQueue, DISPATCH_QUEUE_SERIAL);
    // + 主隊列(1.主線程調用:死鎖;2.其餘線程調用:不會開啓新線程,執行完一個任務,再執行下一個任務)
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    for (NSInteger i=0; i<10; i++) {
        
        dispatch_sync(queue, ^{
            // 追加任務
            [self addTask:i];
        });
    }
    
    NSLog(@"syncExecute end");
}
複製代碼
2.3 GCD 線程間通訊

例如,從非主線程中異步執行完一個操做後,回到主線程更新UI的大體操做以下:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 若是下載結束回到主線程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            // 更新UI
        });
    });
複製代碼
2.4 關於GCD 死鎖

在主線程中,直接執行如下代碼:

dispatch_sync(dispatch_get_main_queue(), ^{
   
});
複製代碼

就會界面卡死,發生GCD 死鎖。緣由就是主線程正在執行dispatch_sync操做(同步,並等待其中block完成),而dispatch_sync操做須要等待主線程執行完當前操做才能將block加入主隊列,這樣就造成了「互相等待」。 一般狀況下,在一個線程正在執行一個串行隊列sQueue上任務的過程當中,再次調用dispatch_sync同步執行這個串行隊列sQueue在上的任務,就會引發死鎖,以下:

- (void)deadLock {
    
    dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_SERIAL);
    
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
    });
    NSLog(@"4");
}

//// 打印日誌(界面卡死)
//2019-01-14 18:07:22.161085+0800 QiMultiThread[469:47692] 1
//2019-01-14 18:07:22.161286+0800 QiMultiThread[469:47692] 4
//2019-01-14 18:07:22.161346+0800 QiMultiThread[469:47707] 2
複製代碼

3、GCD中的其餘經常使用方法

3.1 dispatch_once

通常用來實現建立一個單例,將某個類的實例化部分放在dispatch_once的block中來實現。

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // code to be executed once
    })
複製代碼
3.2 dispatch_after

在一個queue中,延時執行某個操做,好比app啓動後先執行,開屏幕動畫,延時加載界面等。

dispatch_queue_t queue= dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
  // 在queue裏面延遲執行的一段代碼...
});
複製代碼
3.3 dispatch_apply

用dispatch_apply方法能夠執行queue中的一個指定任務(即block)n次。

dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_apply(10, queue, ^(size_t i) {
  NSLog(@"執行 第%lu次", (long)i);
});
複製代碼

注意:若是隊列是併發隊列,則會併發執行block任務,dispatch_apply是一個同步調用,block任務執行n次後才返回。

3.4 dispatch_barrier

dispatch_barrier有兩個方法:dispatch_barrier_async和dispatch_barrier_sync。在同一個隊列中dispatch_barrier方法須要等待其前面全部任務執行完畢,再執行本身,本身執行完以後再執行其後面的任務。

-(void)diapatchBarrier {
    NSLog(@"currentThread: %@", [NSThread currentThread]);
    NSLog(@"---start---");
    
    dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:7];
        NSLog(@"asyncTask_1");
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:5];
        NSLog(@"asyncTask_2");
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier_asyncTask");
        [NSThread sleepForTimeInterval:3];
        
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"asyncTask_4");
    });
    
    NSLog(@"---end---");
}

//// 打印日誌
//2019-01-14 16:56:40.673822+0800 QiMultiThread[401:32253] currentThread: <NSThread: 0x1c42622c0>{number = 1, name = main}
//2019-01-14 16:56:40.674137+0800 QiMultiThread[401:32253] ---start---
//2019-01-14 16:56:40.674364+0800 QiMultiThread[401:32253] ---end---
//2019-01-14 16:56:45.681232+0800 QiMultiThread[401:32340] asyncTask_2
//2019-01-14 16:56:47.682411+0800 QiMultiThread[401:32285] asyncTask_1
//2019-01-14 16:56:47.682631+0800 QiMultiThread[401:32285] barrier_asyncTask
//2019-01-14 16:56:51.688996+0800 QiMultiThread[401:32285] asyncTask_4
複製代碼
3.5 dispatch_group

在串行隊列中,若是想讓一任務A在其餘一系列任務B、C、D完成以後再執行,那麼在這個串行隊列中,將任務A追加在這一些列任務以後就能夠了。可是在並行隊列中,就須要用dispatch_group來實現這波操做。

1)dispatch_group_notify
由於dispatch_group_notify是異步執行的,因此不會阻塞當前線程。

-(void)dispatchGroupNotify {
    
    NSLog(@"currentThread: %@", [NSThread currentThread]);
    NSLog(@"---start---");
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"groupTask_1");
        NSLog(@"currentThread: %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:7];
        NSLog(@"groupTask_2");
        NSLog(@"currentThread: %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:4];
        NSLog(@"groupTask_3");
        NSLog(@"currentThread: %@", [NSThread currentThread]);
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"dispatch_group_Notify block end");
    });
    
    NSLog(@"---end---");
}

//// 打印日誌
//2019-01-14 15:21:15.194094+0800 QiMultiThread[295:9717] ---start---
//2019-01-14 15:21:15.194270+0800 QiMultiThread[295:9717] ---end---
//2019-01-14 15:21:17.198617+0800 QiMultiThread[295:9820] groupTask_1
//2019-01-14 15:21:17.199424+0800 QiMultiThread[295:9820] currentThread: <NSThread: 0x1c4660640>{number = 3, name = (null)}
//2019-01-14 15:21:19.200911+0800 QiMultiThread[295:9825] groupTask_3
//2019-01-14 15:21:19.201224+0800 QiMultiThread[295:9825] currentThread: <NSThread: 0x1c0278640>{number = 4, name = (null)}
//2019-01-14 15:21:22.200290+0800 QiMultiThread[295:9745] groupTask_2
//2019-01-14 15:21:22.200595+0800 QiMultiThread[295:9745] currentThread: <NSThread: 0x1c4666b00>{number = 5, name = (null)}
//2019-01-14 15:21:22.200810+0800 QiMultiThread[295:9745] dispatch_group_Notify 結束

複製代碼

2)dispatch_group_wait
顧名思義,dispatch_group_wait會阻塞當前線程,直到任務都完成時纔會繼續執行以後的代碼。

-(void)dispatchGroupWait {
    
    NSLog(@"currentThread: %@", [NSThread currentThread]);
    NSLog(@"---start---");
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"groupTask_1");
        NSLog(@"currentThread: %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:7];
        NSLog(@"groupTask_2");
        NSLog(@"currentThread: %@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:4];
        NSLog(@"groupTask_3");
        NSLog(@"currentThread: %@", [NSThread currentThread]);
    });
    
    long result = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC));
    NSLog(@"dispatch_group_wait result = %ld", result);
    
    NSLog(@"---end---");
}

//// 打印日誌
//2019-01-14 15:46:37.226768+0800 QiMultiThread[323:15757] ---start---
//2019-01-14 15:46:39.231152+0800 QiMultiThread[323:15795] groupTask_1
//2019-01-14 15:46:39.231367+0800 QiMultiThread[323:15795] currentThread: <NSThread: 0x1c0073600>{number = 3, name = (null)}
//2019-01-14 15:46:41.232170+0800 QiMultiThread[323:15801] groupTask_3
//2019-01-14 15:46:41.232644+0800 QiMultiThread[323:15801] currentThread: <NSThread: 0x1c00736c0>{number = 4, name = (null)}
//2019-01-14 15:46:44.231956+0800 QiMultiThread[323:15827] groupTask_2
//2019-01-14 15:46:44.232328+0800 QiMultiThread[323:15827] currentThread: <NSThread: 0x1c446b440>{number = 5, name = (null)}
//2019-01-14 15:46:44.232468+0800 QiMultiThread[323:15757] dispatch_group_wait result = 0
//2019-01-14 15:46:44.232525+0800 QiMultiThread[323:15757] ---end---
複製代碼

注: dispatch_group_wait中設置了10秒的等待時間,若是group全部任務的執行時間<=10秒就返回0,若是>10秒則返回非0。

3)dispatch_group_enter和dispatch_group_leave
用dispatch_group_enter跟dispatch_group_leave方法,並配合dispatch_async方法使用,能夠代替dispatch_group_async。不過這樣操做更顯麻煩,dispatch_group_enter與dispatch_group_leave兩個方法要配對出現,且group操做結尾仍須要dispatch_group_notify。

-(void)dispatchGroupEnter {
    
    NSLog(@"currentThread: %@", [NSThread currentThread]);
    NSLog(@"---start---");
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:7];
        NSLog(@"asyncTask_1");
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:4];
        NSLog(@"asyncTask_2");
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"dispatch_group_notify block end");
    });
    NSLog(@"---end---");
}
}

//// 打印日誌
//2019-01-14 15:47:56.818998+0800 QiMultiThread[326:16283] currentThread: <NSThread: 0x1c007bc00>{number = 1, name = main}
//2019-01-14 15:47:56.819064+0800 QiMultiThread[326:16283] ---start---
//2019-01-14 15:47:56.819122+0800 QiMultiThread[326:16283] ---end---
//2019-01-14 15:48:00.824143+0800 QiMultiThread[326:16317] asyncTask_2
//2019-01-14 15:48:03.824189+0800 QiMultiThread[326:16314] asyncTask_1
//2019-01-14 15:48:03.824322+0800 QiMultiThread[326:16283] dispatch_group_notify block end

複製代碼
3.6 dispatch_block

以下面將任務(block)添加到隊列中的兩個方法:

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
複製代碼

其中,咱們能夠把第二個參數單獨定義一個dispatch_block_t型變量,再傳入相應方法中:

// 單獨定義一個dispatch_block_t型變量
dispatch_block_t block=dispatch_block_create(0, ^{
  NSLog(@"dispatchBlock_work");
});

// 調用過程 dispatch_async(queue, block);
複製代碼

1)dispatch_block_wait
dispatch_block_wait的執行效果與dispatch_group_wait相似,一樣會阻塞當前線程。下面的代碼中,將wait操做放在了dispatch_async中來執行(且queue爲DISPATCH_QUEUE_CONCURRENT型),避免了阻塞主線程。

- (void)dispatchBlockWait {
    
    dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_block_t block = dispatch_block_create(0, ^{
        NSLog(@"---before---");
        [NSThread sleepForTimeInterval:7];
        NSLog(@"---after---");
    });
    dispatch_async(queue, block);
    
    dispatch_async(queue, ^{
        long result = dispatch_block_wait(block, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
        NSLog(@"dispatch_block_wait result = %ld", result);
    });
}

//2019-01-14 16:34:59.389940+0800 QiMultiThread[382:27805] ---before---
//2019-01-14 16:35:02.396144+0800 QiMultiThread[382:27809] dispatch_block_wait result = 49
//2019-01-14 16:35:06.395332+0800 QiMultiThread[382:27805] ---after---
複製代碼

2)dispatch_block_notify
dispatch_block_notify當觀察的某個block執行結束以後馬上通知提交另外一特定的block到指定的queue中執行,該函數有三個參數,第一參數是須要觀察的block,第二個參數是被通知block提交執行的queue,第三參數是當須要被通知執行的block

- (void)dispatchBlockNotify {
    
    //dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_block_t preBlock = dispatch_block_create(0, ^{
        NSLog(@"preBlock start");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"preBlock end");
    });
    dispatch_async(queue, preBlock);
    dispatch_block_t afterBlock = dispatch_block_create(0, ^{
        NSLog(@"has been notifyed");
    });
    
    dispatch_block_notify(preBlock, queue, afterBlock);
}

//// 打印日誌
//2019-01-14 17:20:33.893522+0800 QiMultiThread[425:37900] preBlock start
//2019-01-14 17:20:35.898765+0800 QiMultiThread[425:37900] preBlock end
//2019-01-14 17:20:35.899008+0800 QiMultiThread[425:37900] has been notifyed
複製代碼

注:此處queue爲DISPATCH_QUEUE_SERIAL或DISPATCH_QUEUE_CONCURRENT都可;當preBlock執行完後,afterBlock會自動提交到queue中執行(queue能夠是其餘自定義隊列)。

3)dispatch_block_cancel 提交到隊列的block,是能夠撤銷的,以下

dispatch_block_t block = dispatch_block_create(0, ^{
        
});
dispatch_async(queue, block);
dispatch_block_cancel(block); // 撤銷一個任務
複製代碼
3.7 dispatch_set_target_queue

dispatch_set_target_queue 函數有兩個參數queue和targetQueue,dispatch_set_target_queue有兩個功能:queue和targetQueue的優先級,targetQueue的優先級更高;targetQueue能夠成爲queue中全部任務的參照隊列,也即queue中的任務將依照targetQueue的類型特色來執行。

dispatch_set_target_queue(queue, targetQueue);
複製代碼

將不一樣隊列中的任務同步的執行:

- (void) dispatchSet2 {
    
    dispatch_queue_t targetQueue = dispatch_queue_create("QiShareQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue1 = dispatch_queue_create("QiShareQueue_1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("QiShareQueue_2", DISPATCH_QUEUE_CONCURRENT);
    
    
    dispatch_set_target_queue(queue1, targetQueue);
    dispatch_set_target_queue(queue2, targetQueue);
    
    dispatch_async(queue1, ^{
        [NSThread sleepForTimeInterval:5];
        NSLog(@"Task_1");
        
    });
    dispatch_async(queue2, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"Task_2");
        
    });
    dispatch_async(queue2, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"Task_3");
        
    });
}

//// 打印日誌
//2019-01-14 17:45:29.920351+0800 QiMultiThread[447:42950] currentThread: <NSThread: 0x1c407c600>{number = 1, name = main}
//2019-01-14 17:45:29.920424+0800 QiMultiThread[447:42950] ---start---
//2019-01-14 17:45:29.920474+0800 QiMultiThread[447:42950] ---end---
//2019-01-14 17:45:34.925556+0800 QiMultiThread[447:42987] Task_1
//2019-01-14 17:45:37.930813+0800 QiMultiThread[447:42987] Task_2
//2019-01-14 17:45:38.936053+0800 QiMultiThread[447:42987] Task_3
複製代碼

上述代碼中,咱們將targetQueue設置爲串行,則queue1與queue2均參照targetQueue的類型特色來執行。

ps:

  1. 異步執行(async)雖然具備開啓新線程的能力,可是並不必定會開啓新線程,是否開啓新線程跟任務所在隊列類型有關;
  2. 在dispatch_barrier_(a)sync方法中,咱們要注意的是不要使用全局併發隊列,DISPATCH_QUEUE_CONCURRENT類型的自定義隊列更合適一些。

參考文章連接,感謝!

工程源碼GitHub地址


小編微信:可加並拉入《QiShare技術交流羣》。

關注咱們的途徑有:
QiShare(簡書)
QiShare(掘金)
QiShare(知乎)
QiShare(GitHub)
QiShare(CocoaChina)
QiShare(StackOverflow)
QiShare(微信公衆號)

推薦文章:
iOS Winding Rules 纏繞規則
iOS 簽名機制
iOS 掃描二維碼/條形碼
iOS 瞭解Xcode Bitcode
iOS 重繪之drawRect
奇舞週刊

相關文章
相關標籤/搜索