GCD之dispatch_async;dispatch_sync;dispatch_async_f;dispatch_sync_f

一、概念与类型

对于GCD来说,所有的执行都放到队列中(queue),队列的特点是FIFO(先提交的先执行)。
GCD的队列分为几种,主队列(main),全局队列(global),用户创建队列(create)
对于全局队列,默认有四个,分为四个优先级
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

DISPATCH_QUEUE_PRIORITY_HIGH :优先级最高,在default,和low之前执行
DISPATCH_QUEUE_PRIORITY_DEFAULT 默认优先级,在low之前,在high之后
DISPATCH_QUEUE_PRIORITY_LOW 在high和default后执行
DISPATCH_QUEUE_PRIORITY_BACKGROUND:提交到这个队列的任务会在high优先级的任务和已经提交到background队列的执行完后执行。官方文档:(the queue is scheduled for execution after all high priority queues have been scheduled and the system runs items on a thread whose priority is set for background status.)

二、dispatch_async与dispatch_sync

  • dispatch_sync
    提交到队列中同步执行
  • dispatch_async
    提交到队列中异步执行,立即返回
  • dispatch_barrier_sync
  • dispatch_barrier_async
    在队列中,栅栏块必须单独执行,不能与其它块并行。这只对队列有意义,因为串行队列中的块总是按顺序逐个来执行的。并发队列中如果发现接下来要处理的块是个栅栏块(barrier block),那么就一直等到当前所有并发块都执行完毕,才会单独执行这个栅栏块。

这是Effective Objective-C 2.0 这本书 中对dispatch_barrier_async的说明,书中有一段示例代码,大概是这样的:

 - (instancetype)init
 {
        self = [super init];
        if (!self) {
              return nil;
        }
        _concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
       return self;
  }

 - (NSString *)name
 {
         __block NSString *name;
        dispatch_sync(_concurrentQueue, ^{
             name = _name;
        });
        return name;
 }

 - (void)setName:(NSString *)name
 {
       dispatch_barrier_async(_concurrentQueue, ^{
             _name = name;
      });
  }

问题来了,以上代码能实现对name属性的读写同步吗?
按道理,是可以的。但事实并不可以,因为在这里苹果给我们挖了一个坑。。。
原因就是 在这里 我们使用的是全局并发队列。。
都是 并发队列 凭啥就不一样呢??

就是说,栅栏块应该在自己创建的并行队列里执行,如果是在串行队列或是全局并行队列中执行,那么就起不到栅栏的作用,和dispatch_async 函数效果一样了。

所以上面代码只要把_concurrentQueue 改成自己创建的

 _concurrentQueue = dispatch_queue_create("com.people.test", DISPATCH_QUEUE_CONCURRENT);

就可以实现 读写的同步了。

隔离队列

再来理解一个隔离队列的概念,也是跟dispatch_barrier_sync和dispatch_barrier_async有关。

例如操作系统中的经典的读者-写者问题,简而言之,我们可以在同一时刻有多个读者,但同一时刻只能有一个线程可以写入。解决方法就是利用GCD的四种 APIs

  • dispatch_sync
  • dispatch_async
  • dispatch_barrier_sync
  • dispatch_barrier_async

我们的想法是读操作可以支持同步和异步,而写操作也能支持异步写入,并且必须确保是写入的是同一个引用。GCD 的 barrier 集合 APIs 提供了解决方案:他们将会一直等到队列中的任务清空,才会继续执行 block。使用 barrier APIs 可以用来限制我们对字典对象的写入,并且确保我们不会在同一时刻进行多个写操作,以及正在写操作时同时进行读操作。看一段代码:

 class IdentityMap<T: Identifiable> {  
var dictionary = Dictionary<String, T>()
let accessQueue = dispatch_queue_create("com.khanlou.isolation.queue", DISPATCH_QUEUE_CONCURRENT)

func object(withID ID: String) -> T? {
    var result: T? = nil
    dispatch_sync(accessQueue, {
        result = dictionary[ID] as T?
    })
    return result
}

func addObject(object: T) {
    dispatch_barrier_async(accessQueue, {
        dictionary[object.ID] = object
    })
}
}

dispatch_sync 将会分发 block 到我们的隔离队列上然后等待其执行完毕。通过这种方式,我们就实现了同步读操作(如果我们搞成异步读取,getter 方法就需要一个 completion block)。因为 accessQueue 是并发队列,这些同步读取操作可以并发执行,也就是允许同时读。

dispatch_barrier_async 将分发 block 到隔离队列上,async 异步部分意味着会立即返回,并不会等待 block 执行完毕。这对性能是有好处的,但是在一个写操作后立即执行一个读操作会导致读到一个半成品的数据(因为可能写操作还未完成就开始读了)。

dispatch_barrier_async 中的 barrier 部分意味着:只要 barrier block 进入队列,并不会立即执行,而是会等待该队列其他 block 执行完毕后再执行。所以在这点上就保证了我们的 barrier block 每次都只有它自己在执行。而所有在他之后提交的 block 也会一直等待这个 barrier block 执行完再执行。

传入 dispatch_barrier_async() 函数的 queue,必须是用 dispatch_queue_create 创建的并发 queue。如果是串行 queue 或者是 global concurrent queues,这个函数就会变成 dispatch_async() 了

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

推荐阅读更多精彩内容

  • 简介 GCD(Grand Central Dispatch)是在macOS10.6提出来的,后来在iOS4.0被引...
    sunmumu1222阅读 841评论 0 2
  • 本篇博客共分以下几个模块来介绍GCD的相关内容: 多线程相关概念 多线程编程技术的优缺点比较? GCD中的三种队列...
    dullgrass阅读 37,823评论 30 236
  • iOS中GCD的使用小结 作者dullgrass 2015.11.20 09:41*字数 4996阅读 20199...
    DanDanC阅读 808评论 0 0
  • 我们知道在iOS开发中,一共有四种多线程技术:pthread,NSThread,GCD,NSOperation: ...
    请叫我周小帅阅读 1,476评论 0 1
  • 最近常常晚上一点左右被痛苦的咳嗽憋醒。刚开始特别难受,坐起来后做一些自己白天没来得及做的不正经的或者想做的事情,半...
    学会取悦自己阅读 226评论 0 0