【问题标题】:synchronized block within dispatch_asyncdispatch_async 中的同步块
【发布时间】:2013-10-12 06:40:44
【问题描述】:

我见过将异步调度到主队列或私有调度队列(串行)的代码,然后在调度代码块中是@synchronized。你想在什么情况下这样做?串行队列不是已经提供了所需的同步吗?

可以用另一个 GCD 调度替换同步块吗?

【问题讨论】:

    标签: ios objective-c grand-central-dispatch


    【解决方案1】:

    @synchronized() 确保包含的代码(对于作为@synchronized 的参数的给定标记)一次仅在一个线程上运行。

    提交到串行队列的块一次执行一个,即。在完成执行之前提交的所有块之前,不会执行给定块。只要仅从串行队列上运行的代码访问共享资源,就无需同步/锁定对它的访问。然而,仅仅因为给定的队列是串行的,并不意味着其他队列/线程(甚至串行队列!)不会同时运行并访问相同的共享资源。

    使用@synchronized() 是防止这些多个线程/队列同时访问资源的一种方法。请注意,所有访问共享资源的代码都需要用@synchronized()包装。

    是的,您可以使用另一个 GCD 调度来代替同步块。这样做的“GCD 方式”是使用串行队列序列化对共享资源的所有访问。因此,任何时候需要访问共享资源时,您都可以将该代码(根据用例使用dispatch_sync()dispatch_async())分派到与资源关联的串行队列。当然,这意味着资源访问队列必须对访问共享资源的程序的所有部分都是可见/可访问的。 (@synchronized() 本质上存在相同的问题,因为它的锁定令牌必须在需要使用的任何地方都可以访问,但它更容易一些,因为它可以只是一个字符串常量。)

    【讨论】:

      【解决方案2】:

      队列是的,它是同步的,但是如果您访问其中的任何“外部”对象,它们都不会同步。

      如果有很多线程,你知道每个线程都会轮到对象。一个典型的情况是,当您异步执行 CoreData 导入时,您必须 @synchronized 上下文或存储协调器。

      【讨论】:

      • 如果您在私有队列上进行异步核心数据保存,则不必@synchronize。
      • 在私有队列上没有,在这种情况下,如果需要,只能同步存储。
      【解决方案3】:

      线程安全是关于使可变共享状态不可变或不共享。在这种情况下,synchronize 和串行队列是临时取消共享(防止并发访问)的方式,两者都是有效的。

      但是,请考虑在同一对象内有不相交的相关信息集的情况。例如,账单包含 1) 地址(城市、街道等)的一部分,以及 2) 价格、税金、折扣。两者都需要保护以避免不一致的状态(对象 A 设置新街道,而对象 B 读取旧城和新街道),但两者都是不相关的。在这种情况下,您应该使用较低级别的粒度来避免不相关代码之间的阻塞。

      因此,一条规则是:不要对同一对象内不相关的变量集使用同步,因为这会导致它们之间出现不需要的块。您可以使用队列 + 同步,或每组一个队列,但在两个组上不同步。

      关键是synchronize 指的是对象的唯一一个内在锁,并且该令牌只能被持有一次。它实现了与通过一个队列路由所有相关代码相同的目标,不同之处在于您可以有多个队列(因此​​粒度较低),但只有一个内在锁。

      回到你的例子。假设对象被记录为“状态 X 受synchronize 保护”,在队列中使用synchronize 有助于阻止可以访问相同状态的相关方法。所以可能 queue 和 synchronized 保护不同的东西,或者串行队列执行不同的任务。

      更喜欢队列的另一个原因是编写更复杂的模式,例如读写锁。示例:

      NSMutableDictionary *_dic = [NSMutableDictionary new];
      dispatch_queue_t _queue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
      
      - (id) objectForKey:(id)key
      {
          __block obj;
          dispatch_sync(_queue, ^{
              obj = [_dic objectForKey: key];
          });
          return obj;
      }
      
      - (void) setObject:(id)obj forKey:(id)key
      {
          // exclusive access while writing
          dispatch_barrier_async(_queue, ^{
              [_dic setObject:obj forKey:key];
          });
      }
      

      【讨论】:

      • 什么是障碍异步?我从来不知道它的存在。提前致谢。
      • 调度屏障允许您在自定义并发调度队列中创建同步点(它不适用于全局队列)。有 4 个变体:dispatch_barrier_[a]sync_[f]。 f 是运行函数。它们的工作方式如下: 1)您向队列提交一个障碍块。 2)当执行到达屏障块时,队列停止,直到屏障之前提交的所有块完成执行。 3)屏障块单独执行(此时没有其他块正在执行)。 4) 队列照常继续。
      • 我希望我能再次 +1。但是你能举一个例子来说明这个 dispatch_barrier_[a]sync_[f] 的优点吗?
      • 任何语言或实现中的Barriers都是一种同步工具,用于协作并需要在某个点相遇的线程。在数值计算中,有很多情况下,算法的某些部分本质上是顺序的,需要并行计算的结果才能继续。另一个例子是并行合并排序。
      • "你锁定了整个对象以独占使用你的同步代码" "对象仍然可以接收来自其他对象的调用" 这些引用表明你认为@synchronized 阻止了对对象的所有访问。它不是。它锁定与该对象关联的内部锁,但这只会影响尝试获取相同锁的其他代码。也就是说,在同一对象上也使用@synchronized 的其他代码。不使用@synchronized 向对象发送消息的代码不受影响。
      猜你喜欢
      • 1970-01-01
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-21
      • 2014-04-30
      • 2015-05-25
      相关资源
      最近更新 更多