关于dispatch_barrier_async和dispatch_barrier_sync

在iOS多线程中,我们可以用GCD的串行队列来实现同步锁的效果。通过在把任务添加到串行队列中来依次执行,达到同步的效果。
同时GCD又提供了两个函数,来方便我们实现这个同步效果。他们就是dispatch_barrier_asyncdispatch_barrier_sync

先看官方文档


Function dispatch_barrier_async


Submits a barrier block for asynchronous execution and returns immediately.
提交一个异步执行且立即返回的栅栏block

Declaration

void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);

Parameters

The dispatch queue on which to execute the barrier block. The queue is retained by the system until the block has run to completion. This parameter cannot be NULL.
执行栅栏block的调度队列。系统会持有这个队列直到这个block执行完成。这个参数不能为NULL

The barrier block to submit to the target dispatch queue. This block is copied and retained until it finishes executing, at which point it is released. This parameter cannot be NULL.
提交到目标调度队列的栅栏block。block会被拷贝和持有直到它执行完成,完成后会被释放。这个参数不能为NULL

Discussion

Calls to this function always return immediately after the block has been submitted and never wait for the block to be invoked. When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the barrier block executes by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.

当这个block被提交到队列后就立即返回,并不会等待这个block的调用。当栅栏block到达指定的并发队列的时候,这个block不会立即执行。相反,这个队列会等待当前正在执行的blocks完成执行。这时,栅栏block自行执行。一些后面提交的blocks会等到栅栏block执行完成之后才会开始执行。

The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_async function.

指定的queue应该是你通过函数 dispatch_queue_create创建的并发队列。如果你传入一个串行队列或者全局并发队列。这个方法就像dispatch_async 一样。


Function dispatch_barrier_sync


Submits a barrier block object for execution and waits until that block completes.
提交一个栅栏block执行,等待这个block执行完成。

Declaration

void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);

Parameters

The dispatch queue on which to execute the barrier block. This parameter cannot be NULL.
执行栅栏block的指定队列。参数不能为NULL

The barrier block to be executed. This parameter cannot be NULL.
执行的栅栏block。参数不能为NULL

Discussion

Submits a barrier block to a dispatch queue for synchronous execution. Unlike dispatch_barrier_async, this function does not return until the barrier block has finished. Calling this function and targeting the current queue results in deadlock.
提交一个同步执行的栅栏block到指定队列。不像dispatch_barrier_async,这个方法直到栅栏block执行完成后才会返回。调用这个函数在当前线程会导致死锁。
注:队列引起的循环等待导致死锁

When the barrier block reaches the front of a private concurrent queue, it is not executed immediately. Instead, the queue waits until its currently executing blocks finish executing. At that point, the queue executes the barrier block by itself. Any blocks submitted after the barrier block are not executed until the barrier block completes.
当这个block被提交到队列后就立即返回,并不会等待这个block的调用。当栅栏block到达指定的并发队列的时候,这个block不会立即执行。相反,这个队列会等待当前正在执行的blocks完成执行。这时,栅栏block自行执行。一些后面提交的blocks会等到栅栏block执行完成之后才会开始执行

The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_sync function.
指定的queue应该是你通过函数 dispatch_queue_create创建的并发队列。如果你传入一个串行队列或者全局并发队列。这个方法就像dispatch_sync 一样。

Unlike with dispatch_barrier_async, no retain is performed on the target queue. Because calls to this function are synchronous, it "borrows" the reference of the caller. Moreover, no Block_copy is performed on the block.

不像 dispatch_barrier_async,目标队列上不会执行retain操作。因为调用这个函数是同步,他不会被调用者“借用”。此外,对这个block不会执行Block_copy

As an optimization, this function invokes the barrier block on the current thread when possible.
为了优化,这个函数应该尽可能的调用当前线程的栅栏block。

其实把整个文档一翻译完,我想要说的就已经全部包括了,所以遇到问题,先看官方的文档是不错的选择。

另外我想说一下在iOS中用得比较多的一个场景,多读单写。

对于一个对象中的某个属性,我们可以自定义两个方法来实现对其的多读单写操作。

-(NSUInteger)ticketCount {
    __block NSUInteger count;
    //因为要立即返回,所以我们用dispatch_sync
    dispatch_sync(_concurrent_queue, ^{
        count = self->_ticketCount;
    });
    return count;
}

- (void)setTicketCount:(NSUInteger)ticketCount {
    //对于写操作,我们这里用dispatch_barrier_async,用栅栏函数实现线程安全
    dispatch_barrier_async(_concurrent_queue, ^{
        if (ticketCount != self->_ticketCount) {
            self->_ticketCount = ticketCount;
        }
    });
}

其实一般我们不会通过属性的getter和setter方法来写多读单写,一般会自己定义两个方法,在这两个方法中,不一定仅仅是简单的读写,也可以定义一些自己的逻辑。例如我们卖火车票的一个例子。


-(NSUInteger)ticketCount {
   __block NSUInteger count;
   dispatch_sync(_concurrent_queue, ^{
       count = self->_ticketCount;
   });
   return count;
}
-(NSInteger)sellTicketCount:(NSUInteger)count withUserId:(NSUInteger)uid {
   __block NSInteger sellCount;
   dispatch_barrier_sync(_concurrent_queue, ^{
       if ([self queryUserHaveBoughtWithUserId:uid]) {
           //是否购买过
           sellCount = -1; //-1代表已经买过票了
       } else {
           if (count > self->_ticketCount) {
               sellCount = self->_ticketCount;
               self->_ticketCount = 0;
           } else {
               sellCount = count;
               self->_ticketCount = self->_ticketCount - count;
           }

           BoughtUser *buyer = [[BoughtUser alloc] init];
           buyer.uid = uid;
           buyer.count = sellCount;
           [self->_haveBoughtUsers addObject:buyer];
       }
   });
   return sellCount;
}

- (BOOL)refundTicketCount:(NSUInteger)count withUserId:(NSUInteger)uid {
   __block BOOL isSuccess = NO;
   dispatch_barrier_sync(_concurrent_queue, ^{
       if ([self queryUserBoughtCountWithUserId:uid] == count) {
           //只能退还全部车票
           self->_ticketCount = self->_ticketCount + count;
           isSuccess = YES;
       }
   });
   return isSuccess;
}

这也算一个多读单写的具体实现。这里因为要返回购买后剩余的票数,所以只能用dispatch_barrier_sync函数。

需要注意的一定要传入自己创建的并发队列

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359

推荐阅读更多精彩内容

  • Execute code concurrently on multicore hardware by submit...
    ngugg阅读 588评论 0 1
  • 简介 在iOS中,我们需要将非UI且耗时的任务放在主线程当中执行,同时确保在任务完成时进行回调。常用的三种实现多线...
    adduct阅读 384评论 0 1
  • 一:base.h 二:block.h 1. dispatch_block_flags:DISPATCH_BLOCK...
    小暖风阅读 2,437评论 0 0
  • NSThread 第一种:通过NSThread的对象方法 NSThread *thread = [[NSThrea...
    攻城狮GG阅读 803评论 0 3
  • 2018年10月8日 星期一 晴 假期结束,打个电话。 却听电话那头的声音慵懒,好像刚被铃声叫醒。 呀呀,假日...
    也凉阅读 110评论 0 3