Objective-C中的同步线程的锁

概述

在多线程编程中往往会遇到多个线程同时访问共享的资源,这种情况我们需要通过同步线程来避免。也就是给线程加锁。 
因为Objective-CC语言的超集。,严格的来说是真超集。所以C语言当中的pthread互斥锁在Objective-C中也可以使用,但是Objective-C中定义了本身自己的锁对象和锁协议,所以本篇介绍Objective-C中的锁。

NSLock

NSLocking协议

@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
 
 
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

后面介绍的几种锁类型NSLock,NSConditionLock,NSRecursiveLock,都实现了该协议可以统一用lockunlock进行加锁与解锁。


NSLock使用

    NSLock *lock = [[NSLock alloc] init];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [lock lock];                //获取锁
        NSLog(@"lock success");
        sleep(5);
        NSLog(@"sleep end");
        [lock unlock];              //放弃之前获取的锁
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [lock lock];               //获取锁,如果获取不到阻塞当前线程直到获取到锁
        NSLog(@"remove lock");
        [lock unlock];             //放弃获取到的锁
    });
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出结果

 lock success
 sleep end
 remove lock
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

NSLock对象发送lock消息时先检查能不能获取到这个锁,如果此时在其他线程中正在使用这个锁那么阻塞当前线程一直等待其他线程使用完这个锁unlock放弃这个锁后,才可以在自己当前的线程重新获取到,线程恢复。如果一直获取不到这个锁那么线程将一直阻塞,所以lockunlock这两个方法一定要成对出现。当然还有不阻塞调用锁的线程方法tryLocklockBeforeDate:


tryLock 
tryLock尝试获取锁,如果获取不到返回NO,反之YES,不会阻塞当前调用它的线程

    NSLock *lock = [[NSLock alloc] init];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [lock lock];                //获取锁
        NSLog(@"lock success");
        sleep(5);
        NSLog(@"sleep end");
        [lock unlock];              //放弃之前获取的锁
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        if ([lock tryLock])         //尝试获取锁,如果获取不到返回NO,不会阻塞该线程
        {
            NSLog(@"remove lock");
            [lock unlock];
        }
        else{
            NSLog(@"obtain failure");
        }
    });

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

输出结果

lock success
obtain failure
sleep end
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

获取失败是因为当前锁正在其他线程中使用。如果当前锁没有使用那么会返回YES。 
lockBeforeDate 
lockBeforeDate阻塞调用它的线程,如果给定时间内不能够获取锁那么放弃获取并恢复线程。

    NSLock *lock = [[NSLock alloc] init];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [lock lock];                //获取锁
        NSLog(@"lock success");
        sleep(5);
        NSLog(@"sleep end");
        [lock unlock];              //放弃之前获取的锁
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:3];
        if ([lock lockBeforeDate:date]) //尝试在未来的3s内获取锁,并阻塞该线程,如果3s内获取不到恢复线程
        {
            NSLog(@"remove lock");
            [lock unlock];
        }
        else{
            NSLog(@"obtain failure");
        }
    });
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

输出结果

 lock success
 obtain failure
 sleep end
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

如果3s内获取不到则返回NO,否则返回YES

@synchronized

synchronized对一个对象提供锁定和解锁机制。synchronized会阻塞调用它的线程

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        @synchronized (self) {
            [self logging:@"lock success"];
            sleep(5);
            [self logging:@"sleep end"];
        }
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        @synchronized (self)    //等到上面线程执行完后才回执行下面的代码,此时会阻塞当前线程
        {
            [self logging:@"remove lock"];
        }
    });

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

输出结果

 lock success
 sleep end
 remove lock
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

上面对自身对象进行加锁直到上面第一个GCD全部执行结束才会执行下面加锁的代码。 
注意 
Objective-C类自身也是对象所以可以对这些类对象使用synchronized,此时是对整个对象类型进行线程同步。例如:

    @synchronized ([self class]) {
    }
 
 
  • 1
  • 2
  • 1
  • 2

NSConditionLock

NSConditionLock条件锁,获取锁时必须与之前设置解锁的条件一样时才可以获取到,否则当前线程一直阻塞,直到条件一致时线程才可以恢复。 
最典型的例子就是生产者-消费者场景,当数据为空时生产者生产数据此时消费者不能够获取数据,生产者生产数据后,消费者处理数据,直到消费者处理所有数据后,数据又为空,此时生产者继续生产数据。示例代码如下:

    enum {NO_DATA_IN_QUEUE = 0,HAS_DATA_IN_QUEUE};
    NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:NO_DATA_IN_QUEUE];

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (YES) {
            [conditionLock lockWhenCondition:NO_DATA_IN_QUEUE];
            NSLog(@"insert data");
            sleep(3);
            NSLog(@"intset success");
            [conditionLock unlockWithCondition:HAS_DATA_IN_QUEUE];
        }
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (YES) {
            [conditionLock lockWhenCondition:HAS_DATA_IN_QUEUE];
            NSLog(@"delete data");
            sleep(3);
            NSLog(@"delete success");
            [conditionLock unlockWithCondition:NO_DATA_IN_QUEUE];
        }
    });

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

输出结果:

 insert data
 intset success
 delete data
 delete success
 insert data
 intset success
 delete data
 delete success
 ...
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

NSRecursiveLock

NSRecursiveLock递归锁,当我们对一个递归函数同步线程时会在同一个线程多次获取锁,导致线程死锁,NSRecursiveLock可以在同一个线程多次获取锁不会死锁。

NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{

       static void (^recursive)(int count);
        recursive = ^(int count){
            [recursiveLock lock];
            if (count>0) {
                NSLog(@"success lock");
                sleep(3);
                recursive(--count);
            }
            [recursiveLock unlock];
        };
        recursive(3);

    });
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上面这种情况如果使用NSLock在没有解锁时继续获取锁,就会造成死锁导致线程一致堵塞。

NSDistributedLock

NSDistributedLock是Mac多线程开发中的互斥锁解决方案,在此不多做介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Objective-C是一门面向对象的编程语言,它是C语言的扩展,常用于开发iOS和Mac应用程序。在Objective-C线程是用来执行并发任务的执行单元。 Objective-C处理线程的方式有多种,最常用的方式是使用NSThread类或GCD(Grand Central Dispatch)。下面我将简单介绍这两种方式: 1. NSThread:NSThreadObjective-C封装的线程类,可以手动创建和管理线程。你可以通过创建NSThread对象,并调用start方法来启动线程。NSThread提供了一些方法,如sleep、exit、cancel等,用于线程的控制和管理。 示例代码: ``` NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMethod) object:nil]; [myThread start]; ``` 2. GCD(Grand Central Dispatch):GCD是一种更现代化的多线程编程方式,它提供了一种高效且易于使用的并发模型。GCD使用队列(dispatch queue)来管理任务的执行,分为串行队列和并发队列两种类型。 示例代码: ``` // 创建并发队列 dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 在并发队列上异步执行任务 dispatch_async(concurrentQueue, ^{ // 执行任务的代码 }); ``` 以上是Objective-C处理线程的两种常见方式,具体选择哪一种取决于你的需求和偏好。需要注意的是,在iOS开发,为了确保UI的响应性,UI的更新必须在主线程(也称为主队列)上执行,可以使用GCD的主队列来实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值