iOS多线程安全(一)

多线程访问同一个对象,经常会出现意料之外的结果。

这里就从atomic与nonatomic讲起。

atomic

atomic能从一定程度上保证线程安全,但是大部分的情况下并没不能完全保证线程安全。

首先我们看看如果将一个属性设置为atomic的时候,编译器帮我们做了什么?

  • 生成原子操作的getter和setter方法

当线程A执行setter方法时,线程B如果需要执行getter方法必须等线程ASetter方法结束后才能执行。

atomic内部实现:

// setter
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
    // ...
    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;        
        slotlock.unlock();
    }
    // ...
}

// getter
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    // ...
    if (!atomic) return *slot;

    // Atomic retain release world
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();
    // ...
}

对于没法在CPU一次读写操作中完成的setter和getter方法,就需要考虑其在多线程中的安全性,否则可能导致crash。
系统是通过自旋锁的方式来保证CPU多次读写的执行顺序。

比如32位的系统中:
一个Bool值占1字节,可以在一次读写操作中完成赋值或取值,因此是线程安全的。
一个指针占4字节,也可以在一次读写操作中完成赋值或取值,因此是线程安全的。
一个double占8字节,则需要两次读写操作才能完成赋值或取值,因此就会存在一个写操作(需两次写操作才能完成),之后就是读操作的可能,导致异常值的现象。

  • 设置Memory Barrier

对于Objective C的实现来说,几乎所有的加锁操作最后都会设置memory barrier,atomic本质上是对getter,setter加了锁,所以也会设置memory barrier。

memory barrier能够保证内存操作的顺序,按照我们代码的书写顺序来。听起来有点不可思议,事实是编译器会对我们的代码做优化,在它认为合理的场景改变我们代码最终翻译成的机器指令顺序。也就是说如下代码:

self.intA = 0;  //line 1
self.intB = 1;  //line 2

编译器可能在一些场景下先执行line2,再执行line1,因为它认为A和B之间并不存在依赖关系,虽然在代码执行的时候,在另一个线程intA和intB存在某种依赖,必须要求line1先于line2执行。

如果设置property为atomic,也就是设置了memory barrier之后,就能够保证line1的执行一定是先于line2的,当然这种场景非常罕见,一则是出现变量跨线程访问依赖,二是遇上编译器的优化,两个条件缺一不可。这种极端的场景下,atomic确实可以让我们的代码更加多线程安全一点,但我写iOS代码至今,还未遇到过这种场景,较大的可能性是编译器已经足够聪明,在我们需要的地方设置memory barrier了。

nonatomic

大部分情况下,我们会选择nonatomic作为属性的attribute,因为它能有效避免线程锁带来的性能消耗,而大部分的多线程安全问题也需要我们自己来完成线程安全。

参考链接:
http://mrpeak.cn/blog/ios-thread-safety/

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,887评论 1 32
  • 医榻上的病者 望穿了健康的背影 留守的孩子 想长长双手去扯遥远母亲的衣襟 田地干涸的唇 等着一滴雨的垂临 被贫瘠撕...
    陈严红阅读 2,312评论 2 5
  • 今天天气十分的炎热,我还是一如既往的准备着医院的考试,貌似忽略了儿子,小家伙也快期末考试了,我十分着急他的成绩...
    纪胜熙阅读 1,859评论 0 0
  • D10 ~ LESSON 10 今天一整天,我去任何地方都毫无疑问地打开双手去接收。当我意识到我不用做任何事情便可...
    梅晓云阅读 3,696评论 0 0
  • 凌晨4点睡的,早上10点醒来,脑袋昏昏成成。我俩的感冒稍有好转,但还是很难受。 我们想出门赶在中午时间,去Bett...
    MrCooper阅读 2,828评论 0 0

友情链接更多精彩内容