美文网首页
线程安全三要素

线程安全三要素

作者: 哦小小树 | 来源:发表于2020-04-27 00:16 被阅读0次

0x01 什么是线程安全

当我们碰到问题时,总会去考虑这个操作在多线程下是不是线程安全的。由此我们总能考虑到一个东西:锁

通过锁我们可以实现数据的同步操作。但这就是线程安全吗?

为此笔者查阅了一些资料得到一些关键词:

线程安全的三个特性:

  • 原子性
    一个操作(可能包含多个子操作)要么全部执行完毕,要不一个都不执行。

  • 可见性(易被忽略,原因参见分析)
    当多个线程并发访问共享变量时,一个线程对共享变量的修改,其他线程能够立即看到。

  • 顺序性
    程序执行的顺序按照代码的先后顺序执行

我们直接理解为:
线程安全其实就是有序的执行原子操作

先保证操作的原子性,然后再保证多个这样的操作能按照我们的要求顺序执行。

可见性 容易被忽略原因分析:
CPU从主内存中读取数据的效率相对来说并不高,现在主流的计算机中都有几级缓存。

每个线程读取共享变量时,都会将该变量加载进其对应的CPU的告诉缓存中,修改该变量后,CPU会立即更新缓存,但并不一定会立即将其写会主内存(实际上写会主内存的时间不可预估)。

此时其他线程(尤其是不在同一个CPU上执行的线程)访问该变量是,从主内存中读到的就是旧的数据,而非第一个线程更新后的数据。

而这一点是操作系统或者说是硬件层面的机制,所以容易被忽略


0x02 iOS atomic线程是否安全分析

atomic作为访问类型,影响到的事修饰属性的setter方法和getter方法
如下示例代码:

声明
@property (nonatomic, copy) NSString *name;
测试代码
Person *p =  [Person new];
for (int i = 0; i < 10000000; i++) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        p.name = [NSString stringWithFormat:@"hello:%d",i];
    });
}

// 会直接crash
(lldb) bt
* thread #5, queue = 'com.apple.root.default-qos', stop reason = EXC_BAD_ACCESS (code=1, address=0x20)
    frame #0: 0x00007fff68c45678 libobjc.A.dylib`objc_release + 24
  * frame #1: 0x0000000100001b20 TestThread`-[Person setName:](self=0x0000000100470830, _cmd="setName:", name=@"hello:284") at Person.h:14:39
    frame #2: 0x0000000100001d62 TestThread`__main_block_invoke(.block_descriptor=0x0000000100651290) at main.m:20:7

通过上面函数调用栈查看可以发现,crash原因定位到 [person setName:]中调用了objc_release

通过下面setNameMRC下的写法,可以很清楚发现多线程下release了同一个_name对象导致crash

- (void)setName:(NSString *)name {
    [_name release];    // 最终多线程同时release一个对象导致crash
    _name = [name copy];
}
修改声明为atomic再次运行代码
@property (atomic, copy) NSString *name;

运行发现不会crash了,但是我们打印最后的用户名

Person *p =  [Person new];
for (int i = 0; i < 1000; i++) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        p.name = [NSString stringWithFormat:@"hello:%d",i];
    });
}
NSLog(@"用户名%@",p.name);

发现每次打印的结果都不同

2020-04-27 00:04:22.147462+0800 TestThread[14203:1179501] 用户名hello:991

这里发现,我们代码的预期是希望打印出hello:999,但是此处却打印的是个随机结果。由此可见atomic并未保证我们线程安全的第三点顺序性。


0x03 总结

atomic线程是不安全的,它的安全性只是体现在读写有序,但是并不能保证外部操作的顺序性。

相关文章

  • 线程安全三要素

    0x01 什么是线程安全 当我们碰到问题时,总会去考虑这个操作在多线程下是不是线程安全的。由此我们总能考虑到一个东...

  • 1_基础知识_chapter04_对象的组合_1_设计线程安全的

    设计线程安全的类的三要素(1) 找出构成对象状态的所有变量(2) 找出约束状态变量的不可变条件(3) 建立对象状态...

  • atomic & nonatomic

    什么是线程安全??? 线程安全:多线程操作共享数据不会出现想不到的结果就是线程安全的,否则,是线程不安全的。 at...

  • ConcurrentHashMap源码设计分析

    二、线程安全(Thread-safe)的集合对象:● Vector 线程安全● HashTable 线程安全● S...

  • HashMap 和 Hashtable 的区别

    线程安全: HashMap 是非线程安全的,而 Hashtable 是线程安全的,因为 Hashtable 内部的...

  • Java 的 StringBuffer 和 StringBuil

    区别就是:线程安全,StringBuffer 是线程安全的,StringBuilder 不是线程安全的。 他俩的实...

  • Java单例模式,线程安全

    懒汉式:线程安全,开销大 双重检查锁:线程安全,根据需求使用 静态内部类锁:线程安全,比较推荐 饿汗式:线程安全,...

  • 2018-06-12 第三十七天

    一、线程安全 线程安全的问题,是针对多线程的程序。单线程的情况下,是不存在线程安全问题。 产生线程安全问题的原因:...

  • 线程安全知多少

    1. 如何定义线程安全 线程安全,拆开来看: 线程:指多线程的应用场景下。 安全:指数据安全。 多线程就不用过多介...

  • JAVA 线程安全

    线程安全定义 一个类在可以被多个线程安全调用时就是线程安全的。 线程安全分类 线程安全不是一个非真即假的命题,可以...

网友评论

      本文标题:线程安全三要素

      本文链接:https://www.haomeiwen.com/subject/lposwhtx.html