头部背景图片
崇山峻岭 |
崇山峻岭 |

多线程中的锁

今日学习笔记:

  1. atomic 作为iOS开发者对这个单次可能不算熟悉, 因为iOS定位于移动端, 为了降低APP的复杂度, 苹果规定UI操作必须要在主线程上操作, 所以我们所有的UI控件属性修饰符必须为nonatomic 这个单次恐怕没有不认识的吧! 那么atomic有啥作用呢, atomic的术语叫做原子的, 即当一个字段设置为atomic时, 这个字段同一时间只能被一个线程去修改, 也叫自旋锁. 下面为示例代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //原子属性的name
    @property (atomic) NSString *name;

    //在多个线程中需要修改
    - (void)updateName {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    self.name = @"wangwang";
    }); //线程1

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    self.name = @"liuliu";
    }); //线程2

    //当代码执行线程1时, name的产生自旋锁, 不允许其他线程来读取, 这时候就避免了在第一次修改的时候,同事进行第二次修改, 当然上面代码仅仅是为了说明问题, 才把两个线程写到一个方法中,实际应用中肯定不是这个样子的, 实际使用会更加复杂.

    }
  2. @synchronized() {}

    自旋锁能保证属性本身不被修改但是下面这种情况可能就不太好办了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    @interface JLUser : NSObject
    //名字
    @property (atomic, strong) NSString *name;
    //职业
    @property (atomic, strong) NSString *profession;

    @end


    //调用此更新代码结果就可能和想的不太一样
    - (void)updateUser {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self updateUserWithName:@"张三" profession:@"老师"];
    });

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    [self updateUserWithName:@"李四" profession:@"程序员"];
    });
    }


    - (void)updateUserWithName:(NSString *)name profession:(NSString *)profession {
    self.user.name = name;
    self.user.profession = profession;
    }

    //执行上面的更新代码后, 得到的user 可能是这样的, 张三 程序员, 李四 老师, 这是为啥呢, 主要是当第一个线程给name赋值写数据时, 第二个线程不能再去给name赋值, 很可能第二个线程就直接给age赋值了, 所以最后可能会导致name和profession对应不上为了解决这种问题我么引入 @synchronized () {}

    @synchronized() {} 是同步所, 小括号中传入一个固定不变的对象, 一般是要锁的对象, 也可以为其他对象, 花括号中为对应需要锁定的读写代码块, 这样当程序进入到花括号中是, 其他线程就无法再次对锁定的代码块进行调用, 上面的代码需要改动的地方如下:

    1
    2
    3
    4
    5
    6
    - (void)updateUserWithName:(NSString *)name profession:(NSString *)profession {
    @synchronized (self.user) {
    self.user.name = name;
    self.user.profession = profession;
    }
    }
  3. NSLock

    锁是进入临界区的基础构件。atomic 属性和 @synchronized 块是为了实现便捷实用的高级别抽象, 而NSLock是更低级别的锁

    使用如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @interface ThreadSafeClass () { 
    NSLock *_lock;
    }
    @end
    -(instancetype)init {
    if(self = [super init]) {
    _lock = [NSLock new];
    }
    return self;
    }
    -(void)safeMethod {
    [_lock lock];
    //进行线程安全的操作代码
    [_lock unlock];
    }
  4. NSRecursiveLock 安全锁, 用法和NSLock基本一样, 不一样的是在同一个类中, 有多个方法使用同一个锁进行同步, 并且一个方法中调用另一个方法, 这时候就需要使用这个锁;示例代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    @interface ThreadSafeClass () { 
    NSRecursiveLock *_lock;
    }
    @end
    -(instancetype)init {
    if(self = [super init]) {
    _lock = [NSRecursiveLock new];
    }
    return self;
    }
    -(void)safeMethod1 {
    [_lock lock];
    //进行线程安全的操作代码
    [self safeMethod2];
    [_lock unlock];
    }
    -(void)safeMethod2 {
    [_lock lock];
    //进行线程安全的操作代码
    [_lock unlock];
    }
  5. NSCondition 条件锁

    苹果官方文档解释如下:

    NSCondition类实现了一个条件变量,其语义遵循用于POSIX样式条件的语义。 条件对象在给定线程中充当锁和检查点。 锁在测试条件并执行由条件触发的任务时保护您的代码。 检查点行为要求在线程继续其任务之前条件为真。 当条件不成立时,线程阻塞。它一直处于阻塞状态,直到另一个线程发出信号通知条件对象;使用如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    -(IBAction)conditionTest:(id)sender
    {
    NSLog(@"begin condition works!");

    //创建一个商品库
    self.products = [[NSMutableArray alloc] init];
    //初始化条件
    self.condition = [[NSCondition alloc] init];

    [NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil];
    }

    //创建一个消费者
    - (void)createConsumenr {
    [self.condition lock];//进入锁定区域

    //判断库存, 如果为空 线程等待signal信号,
    while ([self.products count] == 0) {
    NSLog(@"wait for products");
    [self.condition wait];
    }
    //收到信号后开始消费商品,
    [self.products removeObjectAtIndex:0];
    NSLog(@"comsume a product");
    //离开锁定区
    [self.condition unlock];
    }
    //创建一个生产者
    - (void)createProducter {
    [self.condition lock];//进入锁定区域
    //开始生产
    [self.products addObject:[[NSObject alloc] init]];
    NSLog(@"produce a product");
    //完成生产,发出信号告知其他线程当前完成
    [self.condition signal];
    //跳出锁定区域
    [self.condition unlock];
    }
  1. GCD的线程安全

    暂时还没有研究, 等待补充, 今日有点晚了, 等有时间接着补充!

    ​ 2018-06-12 02:00:00记

  • 1

  • 2

  • 3

  • 4

  • 5