概述

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

因为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多线程开发中的互斥锁解决方案,在此不多做介绍。

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

  1. c#中多线程同步Lock(锁)的研究以及跨线程UI的操作

    本文只针对C#中,多线程同步所用到的锁(lock)作为研究对象.由于想更直观的显示结果,所以,在做demo的时候,就把多线程通过事件操作UI的代码也写了出来,留作备忘和分享吧. 其实多线程的同步,使用 ...

  2. c#中多线程同步Lock(锁)的研究以及跨线程UI的操作 (转)

    https://www.cnblogs.com/tommyheng/p/4104552.html 本文只针对C#中,多线程同步所用到的锁(lock)作为研究对象.由于想更直观的显示结果,所以,在做de ...

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

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

  4. JAVA语言规范-线程和锁章节之同步、等待和通知

    JAVA语言规范:线程和锁 1 同步 java编程语言提供了线程间通信的多种机制.这些方法中最基本的是同步化,此方法是使用监视器实现的.JAVA中每个对象与一个监视器相关联,一个线程可以加锁和解锁监视 ...

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

    转自:http://blog.jobbole.com/44409/ 线程 类std::thread代表一个可执行线程,使用时必须包含头文件<thread>.std::thread可以和普通 ...

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

    JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...

  7. Java线程中的同步

    1.对象与锁 每一个Object类及其子类的实例都拥有一个锁.其中,标量类型int,float等不是对象类型,但是标量类型可以通过其包装类来作为锁.单独的成员变量是不能被标明为同步的.锁只能用在使用了 ...

  8. ReentrantLock+线程池+同步+线程锁

    1.并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行. 2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量 ...

  9. jvm高级特性(6)(线程的种类,调度,状态,安全程度,实现安全的方法,同步种类,锁优化,锁种类)

    JVM高级特性与实践(十三):线程实现 与 Java线程调度 JVM高级特性与实践(十四):线程安全 与 锁优化 一. 线程的实现 线程其实是比进程更轻量级的调度执行单位. 线程的引入,可以把一个检查 ...

随机推荐

  1. java开发过程中几种常用算法

    排序算法 排序算法中包括:简单排序.高级排序 简单排序 简单排序常用的有:冒泡排序.选择排序.插入排序 冒泡排序代码如下: private static void bubbleSrot(int[] a ...

  2. 路飞学城Python-Day7(practise)

    # 1.编码问题# i.请说明python2与python3中的默认编码是什么?# python2中的默认编码是ASCII码,只能识别英文等其他字符# python3中的默认编码是utf-8# ii. ...

  3. sql拼接

    with t as( select 'Charles' parent, 'William' child union select 'Charles', 'Harry' union select 'An ...

  4. NOIp2018模拟赛三十八

    爆〇啦~ A题C题不会写,B题头铁写正解: 随手过拍很自信,出分一看挂成零. 若要问我为什么?gtmdsubtask! 神tm就一个subtask要么0分要么100,结果我预处理少了一点当场去世 难受 ...

  5. CF451E Devu and Flowers (组合数学+容斥)

    题目大意:给你$n$个箱子,每个箱子里有$a_{i}$个花,你最多取$s$个花,求所有取花的方案,$n<=20$,$s<=1e14$,$a_{i}<=1e12$ 容斥入门题目 把取花 ...

  6. svn 验证位置失败 Authorization failed

    进入svn的conf目录下 修改svnserve.conf [general] anon-access = none  #未登录用户不允许访问 auth-access = write  passwor ...

  7. docker私有仓库yum安装 docker-registry

    [root@riyimei-node1:/root]> yum install docker-registryLoaded plugins: fastestmirror, langpacksLo ...

  8. python 面向对象 类方法,静态方法,property

    property 内置装饰器函数 只在面向对象使用 把方法当初属性使用(方法不加参数) 例子: class Rectangle: def __init__(self,long,wide,color): ...

  9. 洛谷 P1193 洛谷团队训练VS传统团队训练

    P1193 洛谷团队训练VS传统团队训练 题目背景 “在中学的信息学教育领域,洛谷无疑是一个相当受欢迎的辅助网站.同时有百余所学校正在通过洛谷进行信息学竞赛(以后简称OI)的教育.洛谷之所以如此受欢迎 ...

  10. flume 读取kafka 数据

    本文介绍flume读取kafka数据的方法 代码: /************************************************************************* ...