Objective-C中的同步线程的锁
概述
在多线程编程中往往会遇到多个线程同时访问共享的资源,这种情况我们需要通过同步线程来避免。也就是给线程加锁。
因为Objective-C是C语言的超集。,严格的来说是真超集。所以C语言当中的pthread互斥锁在Objective-C中也可以使用,但是Objective-C中定义了本身自己的锁对象和锁协议,所以本篇介绍Objective-C中的锁。
NSLock
NSLocking协议
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
后面介绍的几种锁类型NSLock,NSConditionLock,NSRecursiveLock,都实现了该协议可以统一用lock与unlock进行加锁与解锁。
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放弃这个锁后,才可以在自己当前的线程重新获取到,线程恢复。如果一直获取不到这个锁那么线程将一直阻塞,所以lock与unlock这两个方法一定要成对出现。当然还有不阻塞调用锁的线程方法tryLock与lockBeforeDate:
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中的同步线程的锁的更多相关文章
- c#中多线程同步Lock(锁)的研究以及跨线程UI的操作
本文只针对C#中,多线程同步所用到的锁(lock)作为研究对象.由于想更直观的显示结果,所以,在做demo的时候,就把多线程通过事件操作UI的代码也写了出来,留作备忘和分享吧. 其实多线程的同步,使用 ...
- c#中多线程同步Lock(锁)的研究以及跨线程UI的操作 (转)
https://www.cnblogs.com/tommyheng/p/4104552.html 本文只针对C#中,多线程同步所用到的锁(lock)作为研究对象.由于想更直观的显示结果,所以,在做de ...
- JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制
JAVA之旅(十四)--静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.静态同步函数的锁是clas ...
- JAVA语言规范-线程和锁章节之同步、等待和通知
JAVA语言规范:线程和锁 1 同步 java编程语言提供了线程间通信的多种机制.这些方法中最基本的是同步化,此方法是使用监视器实现的.JAVA中每个对象与一个监视器相关联,一个线程可以加锁和解锁监视 ...
- C++11 中的线程、锁和条件变量
转自:http://blog.jobbole.com/44409/ 线程 类std::thread代表一个可执行线程,使用时必须包含头文件<thread>.std::thread可以和普通 ...
- JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this
JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...
- Java线程中的同步
1.对象与锁 每一个Object类及其子类的实例都拥有一个锁.其中,标量类型int,float等不是对象类型,但是标量类型可以通过其包装类来作为锁.单独的成员变量是不能被标明为同步的.锁只能用在使用了 ...
- ReentrantLock+线程池+同步+线程锁
1.并发编程三要素? 1)原子性 原子性指的是一个或者多个操作,要么全部执行并且在执行的过程中不被其他操作打断,要么就全部都不执行. 2)可见性 可见性指多个线程操作一个共享变量时,其中一个线程对变量 ...
- jvm高级特性(6)(线程的种类,调度,状态,安全程度,实现安全的方法,同步种类,锁优化,锁种类)
JVM高级特性与实践(十三):线程实现 与 Java线程调度 JVM高级特性与实践(十四):线程安全 与 锁优化 一. 线程的实现 线程其实是比进程更轻量级的调度执行单位. 线程的引入,可以把一个检查 ...
随机推荐
- PHP mysql_fetch_array得不到数据
好久没写PHP了...为了数据库课设开始了PHP速成之旅... 光是简单的注册功能就坑了我好几个小时,不过万事开头难嘛...之后应该会快起来的. 先说一下昨天遇到的小坑, 我的表单是这么写的 < ...
- SpringCloud学习笔记(19)----Spring Cloud Netflix之服务网关Zuul自定义过滤器
zuul不仅只是路由,还可以自定义过滤器来实现服务验证. 实现案例:自定义过滤器,检验头部是否带有token,如果token=wangx,则通过校验,若不存在或不为wangx则返回提示token错误. ...
- 《Unix环境高级编程》读书笔记 第11章-线程
1. 引言 了解如何使用多个控制线程在单进程环境中执行多个任务. 不管在什么情况下,只要单个资源需要在多个用户键共享,就必须处理一致性问题. 2. 线程概念 典型的Unix进程可以看成只有一个控制线程 ...
- BZOJ 3881 [COCI2015]Divljak (Trie图+Fail树+树链的并+树状数组维护dfs序)
题目大意: Alice有n个字符串S_1,S_2...S_n,Bob有一个字符串集合T,一开始集合是空的. 接下来会发生q个操作,操作有两种形式: “1 P”,Bob往自己的集合里添加了一个字符串P. ...
- LaTeX 写算法伪码
本系列文章由 @YhL_Leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/50054953 LaTeX写算法伪码,需 ...
- fileOP
public void getFileDir(String filePath) { try{ this.tv.setText("当前路径:"+filePath);// 设置当前所在 ...
- Linux修改Linux默认打开方式
从总体上讲 /etc/gnome/defaults.list 保存了全局的打开方式 /.local/share/applications/mimeapps.list 保存了个人的打开方式当两着不一致是 ...
- 利用hive源码解析sql查了哪些表哪些字段
在hiveserver2中使用了org.apache.hadoop.hive.ql.parse.BaseSemanticAnalyzer对抽象语法树(AST)进行语义分析,分析的过程可以得出hive查 ...
- Python 入门学习 -----变量及基础类型(元组,列表,字典,集合)
Python的变量和数据类型 1 .python的变量是不须要事先定义数据类型的.能够动态的改变 2. Python其中一切皆对象,变量也是一个对象,有自己的属性和方法 我们能够通过 来查看变量的类型 ...
- POJ 3204 网络流的必须边
思路: 求一遍网络流 在残余网络上DFS 从起点DFS 从终点把边反向DFS 一个边跟起点连通 跟终点反向的边连通 ans++ 注:此题不能用tarjan 因为有边权为0的边 //By SiriusR ...