Objective-C中的同步线程的锁

概述

在多线程编程中往往会遇到多个线程同时访问共享的资源,这种情况我们需要通过同步线程来避免。也就是给线程加锁。

因为Objective-CC语言的超集。,严格的来说是真超集。所以C语言当中的pthread互斥锁在Objective-C中也可以使用,但是Objective-C中定义了本身自己的锁对象和锁协议,所以本篇介绍Objective-C中的锁。

NSLock

NSLocking协议

@protocol NSLocking
- (void)lock;
- (void)unlock;
@end

后面介绍的几种锁类型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];             //放弃获取到的锁
    });

输出结果

 lock success
 sleep end
 remove lock

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");
        }
    });

输出结果

lock success
obtain failure
sleep end

获取失败是因为当前锁正在其他线程中使用。如果当前锁没有使用那么会返回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");
        }
    });

输出结果

 lock success
 obtain failure
 sleep end

如果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"];
        }
    });

输出结果

 lock success
 sleep end
 remove lock

上面对自身对象进行加锁直到上面第一个GCD全部执行结束才会执行下面加锁的代码。

注意

Objective-C类自身也是对象所以可以对这些类对象使用synchronized,此时是对整个对象类型进行线程同步。例如:

    @synchronized ([self class]) {
    }

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];
        }
    });

输出结果:

 insert data
 intset success
 delete data
 delete success
 insert data
 intset success
 delete data
 delete success
 ...

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);

    });

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

NSDistributedLock

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

时间: 2024-08-13 18:10:33

Objective-C中的同步线程的锁的相关文章

JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制

JAVA之旅(十四)--静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.静态同步函数的锁是class对象 我们在上节验证了同步函数的锁是this,但是对于静态同步函数,你又知道多少呢? 我们做一个这样的小实验,我们给show方法加上static关键字去修饰 private static synchronized void show() { if (tick > 0) { try { Thread

【java并发】(2) Java线程同步:synchronized锁住的是代码还是对象

在Java中,synchronized关键字是用来控制线程同步的,就是在多线程的环境下,控制synchronized代码段不被多个线程同时执行.synchronized既可以加在一段代码上,也可以加在方法上. 关键是,不要认为给方法或者代码段加上synchronized就万事大吉,看下面一段代码: class Sync { public synchronized void test() { System.out.println("test开始.."); try { Thread.sle

C++11 中的线程、锁和条件变量

转自:http://blog.jobbole.com/44409/ 线程 类std::thread代表一个可执行线程,使用时必须包含头文件<thread>.std::thread可以和普通函数,匿名函数和仿函数(一个实现了operator()函数的类)一同使用.另外,它允许向线程函数传递任意数量的参数. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <thread> void func() {    // do some work } int

线程同步(互斥锁与信号量的作用与区别)

“信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里).而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这 个资源.比如对全局变量的访问,有时要加锁,操作完了,在解锁.有的时候锁和信号量会同时使用的” 也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后

【转】【C++】C++ 中的线程、锁和条件变量

线程 类std::thread代表一个可执行线程,使用时必须包含头文件<thread>.std::thread可以和普通函数,匿名函数和仿函数(一个实现了operator()函数的类)一同使用.另外,它允许向线程函数传递任意数量的参数. #include <thread> void func() { // do some work } int main() { std::thread t(func); t.join(); return 0; } 上例中,t 是一个线程对象,函数fu

JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this

JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程)把票陆陆续续的卖完了之后,我们要反思一下,这里面有没有安全隐患呢?在实际情况中,这种事情我们是必须要去考虑安全问题的,那我们模拟一下错误 package com.lgl.hellojava; import javax.security.auth.callback.TextInputCallback

python多线程编程(2): 使用互斥锁同步线程

上一节的例子中,每个线程互相独立,相互之间没有任何关系.现在假设这样一个例子:有一个全局的计数num,每个线程获取这个全局的计数,根据num进行一些处理,然后将num加1.很容易写出这样的代码: # encoding: UTF-8import threadingimport time class MyThread(threading.Thread): def run(self): global num time.sleep(1) num = num+1 msg = self.name+' set

APUE学习笔记——11 线程同步、互斥锁、自旋锁、条件变量

线程同步 同属于一个进程的不同线程是共享内存的,因而在执行过程中需要考虑数据的一致性. 假设:进程有一变量i=0,线程A执行i++,线程B执行i++,那么最终i的取值是多少呢?似乎一定是i=2:其实不然,如果没有考虑线程同步,i的取值可能是1.我们先考虑自加操作的过程:a,首先将内存中i的值copy到寄存器:b,对寄存器中i的copy进行自加:c,将寄存器中自加的结果返回到内存中.回到例子,如果线程A执行完abc三个步骤,线程B在执行者三个步骤,那么结果就应该为2.但是自加不是原子操作,假如执行

Java线程中的同步

1.对象与锁 每一个Object类及其子类的实例都拥有一个锁.其中,标量类型int,float等不是对象类型,但是标量类型可以通过其包装类来作为锁.单独的成员变量是不能被标明为同步的.锁只能用在使用了这些变量的方法上.成员变量可以被声明为volatile,这种方式会影响该变量的原子性,可见性以及排序性.类似的,持有标量变量元素的数组对象拥有锁,但是其中的标量元素却不拥有锁.(也就是说,没有办法将数组成员声明为volatile类型的).如果锁住了一个数组并不代表其数组成员都可以被原子的锁定.也没有