Objective c 这些局部变量如何避免竞争条件?

Objective c 这些局部变量如何避免竞争条件?,objective-c,multithreading,race-condition,Objective C,Multithreading,Race Condition,我遇到了这样一段代码: id target = self.target; SEL selector = self.selector; if([target respondToSelector:@SEL(selector)]) { [target performSelector:@SEL(selector)]; } 作者给出了这样的解释: 我们创建了两个局部变量,以避免在以下可能的执行序列中出现竞争条件: 在某个线程A中调用[target respondsToSelector:selecto

我遇到了这样一段代码:

id target = self.target;
SEL selector = self.selector;
if([target respondToSelector:@SEL(selector)])
{
  [target performSelector:@SEL(selector)];
}
作者给出了这样的解释:

我们创建了两个局部变量,以避免在以下可能的执行序列中出现竞争条件:

  • 在某个线程A中调用
    [target respondsToSelector:selector]

  • 在某些线程B中更改选择器的任一目标

  • 在线程A中调用
    [目标性能选择器:选择器]

  • 使用此代码,即使选择器的任何一个目标发生了更改,也会在正确的目标和选择器上调用performSelector


    我想知道的是:竞争的是哪种资源?这两个局部变量如何帮助避免竞争条件?我真的不知道比赛条件在哪里。提前谢谢

    通常情况下,争用条件是多个线程同时存在的任何情况

    • 争夺某些共享资源
    • 这些线程没有同步对这些资源的访问;因此
    • 结果行为取决于各个线程中事件的精确计时或顺序
    通常,竞争条件问题很难表现出来,因为与许多其他类型的bug不同,表现异常行为所需的事件序列是不确定的,并且可能很少发生。但正如苹果公司在视频中所说,尽管这些竞争条件看起来不太可能,但并没有良性的竞争条件。(幸运的是,视频中描述的线程净化工具aka TSan可以识别多种类型的数据竞争。)

    回到您的示例,想象一下如果线程A上发生以下情况:

    if ([self.target respondToSelector:@SEL(self.selector)]) {
        [self.target performSelector:@SEL(self.selector)];
    }
    
    在此实现中,在线程A确定
    响应选择器
    成功和尝试
    执行选择器
    之间,线程B可能已经滑入并将选择器或目标更改为其他内容。更糟糕的是,如果线程B将这些属性中的任何一个更改为无法执行的内容,应用程序可能会崩溃

    通过在局部变量中复制这些引用(如原始代码片段所示),开发人员消除了这种可能性。因为在线程A中运行的这个例程有到目标和选择器引用的副本,所以现在B是否在选中
    响应选择器
    和调用
    性能选择器
    之间更改属性无关紧要。线程A可以安全地使用其局部变量,从而消除这种特殊的争用条件

    但这并不意味着这是真正的线程安全:

    • 首先,如果多个线程真的对这两个属性执行非同步访问,那么它们至少必须是原子属性。(我不会这么做,原因我将在后面介绍,但在代码段中这样做时,这是最低限度的。)

    • 第二,这两个属性之间存在第二个竞争条件。假设
      self.target
      self.selector
      分别引用一些
      Foo
      对象和实例方法。假设线程A获得了
      Foo
      目标,但在它获得检索选择器的机会之前,线程B滑入并将
      self.target
      self.selector
      更改为一个完全不同的对象,比如一个
      Bar
      对象。然后线程A检索
      self.selector
      ,它现在引用一些
      Bar
      实例方法。当然,如果您问题中的代码使用局部变量来保存
      目标
      选择器
      (因为
      Foo
      目标上的
      Bar
      方法的
      respondsToSelector
      可能会失败),但它也不会做您想要的事情。而这通常也同样糟糕

    • 在目标对象本身中,显然还有其他线程安全注意事项。例如,如果该目标对象不是线程安全的,那么线程B可能正在对目标对象进行变异的过程中,并且当线程A尝试调用选择器方法时,它可能处于内部不一致的状态。因此,您必须确认
      目标
      对象本身是否是线程安全的

    出于这些原因,虽然原始代码段中的模式解决了一个可能由狭隘竞争条件导致的特定崩溃,但它无法解决更广泛的其他问题。因此,典型的解决方案是编写所有人都可以访问这些单独属性(可能还有其他东西)的代码。您可以使用GCD队列(串行队列或读写器模式)或锁定或
    @synchronized
    指令来执行此操作


    而且,回到我之前关于原子属性的评论,如果您使用一些更广泛的同步模式来实现更健壮的线程安全解决方案,那么通常不需要原子属性。

    一般来说,争用条件是多个线程同时运行的任何情况

    • 争夺某些共享资源
    • 这些线程没有同步对这些资源的访问;因此
    • 结果行为取决于各个线程中事件的精确计时或顺序
    通常,竞争条件问题很难表现出来,因为与许多其他类型的bug不同,表现异常行为所需的事件序列是不确定的,并且可能很少发生。但正如苹果公司在视频中所说,尽管这些竞争条件看起来不太可能,但并没有良性的竞争条件。(幸运的是,视频中描述的线程净化工具aka TSan可以识别多种类型的数据竞争。)

    回到你的