iOS 多線程之dispatch_barrier及實現(xiàn)多讀單寫

前言

當(dāng)有個需求,A,B異步請求完成之后才能請求C,D。A,B,C,D都是異步請求。這個用dispatch_group也可以實現(xiàn),只不過比dispatch_barrier麻煩一點。大家可以嘗試用dispatch_group實現(xiàn)一下,這樣能更高的理解dispatch_group。還以使用dispatch_barrier來實現(xiàn)多讀單寫的功能,下面來看看dispatch_barrier怎么來實現(xiàn)這個需求。

dispatch_barrier

dispatch_barrier 作用就是相當(dāng)于柵欄,柵欄前不管多少個異步都要執(zhí)行完畢,才會執(zhí)行柵欄后面的操作。

  • dispatch_barrier_sync

    • 提交一個柵欄函數(shù)在執(zhí)行中,它會等待柵欄函數(shù)執(zhí)行完
  • dispatch_barrier_async

    • 提交一個柵欄函數(shù)在異步執(zhí)行中,它會立馬返回,不需要等值返回

一、dispatch_barrier_async

下面看一下兩者共同點和區(qū)別:

    //barrier 之前的并行線程執(zhí)行2s,線程執(zhí)行完畢。
    //barrier 2s  barrier 后面的線程并行執(zhí)行又是兩秒 總共6s.
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        
        sleep(1);
        NSLog(@"休眠1s,執(zhí)行柵欄前的任務(wù)");
    });
    
    dispatch_async(queue, ^{
       
        sleep(2);
        NSLog(@"休眠2s,執(zhí)行柵欄前的任務(wù)");
    });
    
    //柵欄前所有任務(wù)執(zhí)行完畢,才開始執(zhí)行這個方法
    dispatch_barrier_async(queue, ^{
    
        sleep(2);
        NSLog(@"執(zhí)行柵欄的任務(wù)");
    });
    NSLog(@"執(zhí)行柵欄后不在一個隊列方法");
    //執(zhí)行完柵欄的方法,
    dispatch_async(queue, ^{
        
        sleep(1);
        NSLog(@"休眠1s,執(zhí)行柵欄后的任務(wù)");
    });
  
    dispatch_async(queue, ^{
    
        sleep(2);
        NSLog(@"休眠2s,執(zhí)行柵欄后的任務(wù)");
    });

多次執(zhí)行打印結(jié)果如下:

休眠1s,執(zhí)行柵欄前的任務(wù)
休眠2s,執(zhí)行柵欄前的任務(wù)
執(zhí)行柵欄后不在一個隊列方法
執(zhí)行柵欄的方法
休眠1s,執(zhí)行柵欄后的任務(wù)
休眠2s,執(zhí)行柵欄后的任務(wù)

執(zhí)行結(jié)果表明:

  • 并行執(zhí)行柵欄前的所有任務(wù)
  • dispatch_barrier_async異步方法,不阻塞當(dāng)前線程,所以先執(zhí)行柵欄后不在一個隊列方法
  • 執(zhí)行柵欄里任務(wù)
  • 并行執(zhí)行柵欄后的所有任務(wù)

二、dispatch_barrier_async

    //barrier 之前的并行線程執(zhí)行2s,線程執(zhí)行完畢。
    //barrier 2s  barrier 后面的線程并行執(zhí)行又是兩秒 總共6s.
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        
        sleep(1);
        NSLog(@"休眠1s,執(zhí)行柵欄前的任務(wù)");
    });
    
    dispatch_async(queue, ^{
       
        sleep(2);
        NSLog(@"休眠2s,執(zhí)行柵欄前的任務(wù)");
    });
    
    //柵欄前所有任務(wù)執(zhí)行完畢,才開始執(zhí)行這個方法
    dispatch_barrier_sync(queue, ^{
    
        sleep(2);
        NSLog(@"執(zhí)行柵欄的任務(wù)");
    });
    NSLog(@"執(zhí)行柵欄后不在一個隊列方法");
    //執(zhí)行完柵欄的方法,
    dispatch_async(queue, ^{
        
        sleep(1);
        NSLog(@"休眠1s,執(zhí)行柵欄后的任務(wù)");
    });
  
    dispatch_async(queue, ^{
    
        sleep(2);
        NSLog(@"休眠2s,執(zhí)行柵欄后的任務(wù)");
    });

多次執(zhí)行打印結(jié)果如下:

并行休眠1s,執(zhí)行柵欄前的任務(wù)
并行休眠2s,執(zhí)行柵欄前的任務(wù)
執(zhí)行柵欄的方法
執(zhí)行柵欄后不在一個隊列方法
并行執(zhí)行休眠1s,執(zhí)行柵欄后的任務(wù)
并行執(zhí)行休眠2s,執(zhí)行柵欄后的任務(wù)

執(zhí)行結(jié)果表明:

  • 并行執(zhí)行柵欄前的所有任務(wù)
  • dispatch_barrier_sync同步,阻塞當(dāng)前線程,所以先執(zhí)行柵欄里任務(wù)
  • 執(zhí)行柵欄后不在一個隊列方法
  • 并行執(zhí)行柵欄后的任務(wù)

實現(xiàn)多讀單寫

一、什么是多讀單寫?

  • 可以同時有多個讀操作,而在讀操作的時候,不能有寫操作。

  • 在寫操作的過程中,不能有其他寫操作,并且在寫操作之前,讀操作都完成。

  • 讀操作是可以并發(fā)執(zhí)行,寫操作與(讀操作、其他寫操作)是互斥的。

二、實現(xiàn)多讀單寫的方式

  1. 加讀寫鎖(pthread_rwlock)來實現(xiàn)
#import <pthread.h>
@interface TKReadWhiteSafeDic() {
    // 聲明一個讀寫鎖
   pthread_rwlock_t  lock;
  // 定義一個并發(fā)隊列
    dispatch_queue_t concurrent_queue;
    // 用戶數(shù)據(jù)中心, 可能多個線程需要數(shù)據(jù)訪問
    NSMutableDictionary *userCenterDic;
}

@end

// 多讀單寫模型
@implementation TKReadWhiteSafeDic

- (id)init {
    self = [super init];
    if (self) {
      //初始化讀寫鎖
      pthread_rwlock_init(&lock,NULL);
      // 創(chuàng)建數(shù)據(jù)容器
       userCenterDic = [NSMutableDictionary dictionary];
      // 通過宏定義 DISPATCH_QUEUE_CONCURRENT 創(chuàng)建一個并發(fā)隊列
      concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

- (id)objectForKey:(NSString *)key {
    //加讀鎖
    pthread_rwlock_rdlock(&_rwlock);
    id obj = [userCenterDic objectForKey:key];
    pthread_rwlock_unlock(&_rwlock);
    return obj;
}

- (void)setObject:(id)obj forKey:(NSString *)key {
     //加寫鎖
    pthread_rwlock_wrlock(&_rwlock);
    [userCenterDic setObject:obj forKey:key];
    pthread_rwlock_unlock(&_rwlock);    
}

  1. 使用dispatch_barrie來實現(xiàn)。
@interface TKReadWhiteSafeDic() {
    // 定義一個并發(fā)隊列
    dispatch_queue_t concurrent_queue;
    
    // 用戶數(shù)據(jù)中心, 可能多個線程需要數(shù)據(jù)訪問
    NSMutableDictionary *userCenterDic;
}

@end

// 多讀單寫模型
@implementation TKReadWhiteSafeDic

- (id)init {
    self = [super init];
    if (self) {
        // 通過宏定義 DISPATCH_QUEUE_CONCURRENT 創(chuàng)建一個并發(fā)隊列
        concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);
        // 創(chuàng)建數(shù)據(jù)容器
        userCenterDic = [NSMutableDictionary dictionary];
    }
    return self;
}

- (id)objectForKey:(NSString *)key {
    __block id obj;
    // 同步讀取指定數(shù)據(jù)
    dispatch_sync(concurrent_queue, ^{
        obj = [userCenterDic objectForKey:key];
    });
    return obj;
}

- (void)setObject:(id)obj forKey:(NSString *)key {
    // 異步柵欄調(diào)用設(shè)置數(shù)據(jù)
    dispatch_barrier_async(concurrent_queue, ^{
        [userCenterDic setObject:obj forKey:key];
    });
}

多讀單寫的兩個困惑點解釋一下:

  • 讀操作為啥同步dispatch_sync

讀的話通常都是直接想要結(jié)果,需要同步返回結(jié)果,如果是異步獲取的話就根網(wǎng)絡(luò)請求一樣了。

  • 寫操作為啥異步dispatch_barrier_async

寫操作是因為不需要等待寫操作完成,所以用異步。

結(jié)尾

這個大家可以自己琢磨,做做Demo對dispatch_barrier能有更好的理解。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容