原始值类型属性的线程安全性 - 目标C.

时间:2017-01-12 18:22:56

标签: objective-c multithreading crash primitive

问题

我正在开发一个项目,我担心对象属性的线程安全性。我知道当属性是xlim=c(-5,5)这样的对象时,我可以遇到多个线程同时读写的情况。在这种情况下,您可能会损坏读取,应用程序将崩溃或导致数据损坏。

我的问题是原始值类型属性,例如NSString s或BOOL s。我想知道我是否可以进入类似的情况,当我从多个线程读取和写入时读取损坏的值(并且应用程序将崩溃)?在任何一种情况下,我都会对原因感兴趣。

澄清 - 1/13/17

我最感兴趣的是,如果原始值类型属性由于多个线程同时访问它而不是像NSMutableString,自定义创建对象等对象那样容易崩溃。此外,如果存在差异当访问堆栈上的内存时相对于多线程访问堆。

澄清 - 12/1/17

感谢@Rob指点我这里的答案:stackoverflow.com/a/34386935/1271826!这个答案有一个很好的例子,它表明根据您所使用的体系结构类型(32位与64位),您可以在使用原始属性时获得未定义的结果。

虽然这是回答我问题的重要一步,但我仍然想知道两件事:

  • 如果在堆栈与堆上访问原始值属性时存在多线程差异(如我之前的说明中所述)?
  • 如果您将程序限制为在一个体系结构上运行,那么在访问原始值属性时您是否仍然处于不设防状态?为什么?

我应该注意到,在回答这个问题时,围绕原子与非原子进行了大量的讨论。虽然这通常是一个重要的概念,但这个问题与使用原子属性修饰符或任何其他线程安全方法(如使用GCD)防止未定义的多线程行为几乎没有关系。

1 个答案:

答案 0 :(得分:2)

如果你的原始值类型属性是atomic,那么你确定它不会被破坏,因为你从一个线程读取它而从另一个线程设置它(只要你只使用访问器方法,并且不直接与支持ivar相互作用)。这是atomic的全部目的。并且,正如您所建议的,这仅适用于基本数据类型(或者是不可变和无状态的对象)。但在这些狭隘的案例中,atomic可能很有用。

话虽如此,这与结论是应用程序是线程安全的相差甚远。它只向您保证对该属性的访问是线程安全的。但通常必须在更广泛的背景下考虑线程安全。 (我知道你向我们保证,这不是这种情况,但我对未来的读者有资格,他们很快就会得出atomic足以实现线程安全的结论。通常不是。)

例如,如果您的NSInteger属性为"此缓存对象中有多少项",则NSInteger不仅必须具有其访问权限synchronized,但它也必须与所有与缓存对象的交互同步(例如,"将项添加到缓存"以及"从缓存中移除项目"任务)。并且,在这些情况下,因为您将以某种方式同步与此更广泛的对象的所有交互(例如,使用GCD队列,锁,@synchronized指令,等等),使NSInteger属性{{1}然后变得多余,因此效率不高。

底线,在有限的情况下,atomic可以为基本数据类型提供线程安全性,但在更广泛的背景下查看时通常是不够的。

你后来说你不关心竞争条件。对于它的价值,Apple认为没有良性竞赛这样的事情。参见WWDC 2016视频Thread Sanitizer and Static Analysis(大约14:40)。

无论如何,你建议你只关心价值是否会被破坏或者应用程序是否会崩溃:

  

我想知道是否可以进入类似的情况,当我从多个线程读取和写入时读取损坏的值(应用程序将崩溃)?

最重要的是,如果你在一个线程上读取而在另一个线程上发生变异,那么这个行为就是未定义的。它可能会有所不同建议您尽量避免这种情况。

实际上,它是目标架构的一个功能。例如,在32位x86目标上的64位类型(例如atomic)上,您可以轻松检索损坏的值,其中64位值的一半已设置而另一半未设置。 (例如,请参阅https://stackoverflow.com/a/34386935/1271826。)在处理基元类型时,这仅导致非感知的无效数值。对于指向对象的指针,这显然会带来灾难性的后果。

但即使你处于一个没有出现任何问题的环境中,它也是一种非常脆弱的方法,可以避免同步以实现线程安全。在新的,未预料到的硬件架构上运行或在不同配置下编译时,它可能很容易中断。我们建议您观看Thread Sanitizer and Static Analysis视频以获取更多信息。