iOS 锁
2021-04-26 本文已影响0人
MAXcrazs
锁被使用在多线程中,避免多个线程对同一公共资源的操作
测试对象
LockObj
- LockObj.h
@interface LockObj : NSObject
@property (nonatomic, strong) NSMutableArray *array;
@property (nonatomic, strong) NSLock *lock;
@property (nonatomic, assign) int num3;
+ (int)num1;
+ (int)num2;
+ (int)num4;
@end
- LockObj.m
static int _kNum1 = 0;
static int _kNum2 = 0;
static int _kNum4 = 0;
static NSLock *_klock = nil;
@implementation LockObj
+ (void)load{
_klock = [[NSLock alloc] init];
}
- (instancetype)init{
self = [super init];
if (self) {
self.array = [[NSMutableArray alloc] init];
self.lock = [[NSLock alloc] init];
self.num3 = 0;
}
return self;
}
+ (int)num1{
return _kNum1;
}
+ (int)num2{
return _kNum2;
}
+ (int)num4{
return _kNum4;
}
num3
是对象的属性,_kNum1
,_kNum2
分别是两个全局静态变量
测试函数
- 循环函数
static void _cycleDo(dispatch_block_t block){
for (int i = 0; i < 3000; i++) {
if(block) {block();}
}
}
- 多线程函数
static void _asyncDo(dispatch_block_t block, dispatch_block_t last) {
__block UInt16 finish = 0x0000;
dispatch_async(dispatch_queue_create("1", DISPATCH_QUEUE_CONCURRENT), ^{
_cycleDo(block);
`if(block) {block()
finish = finish | 0x001;
});
dispatch_async(dispatch_queue_create("2", DISPATCH_QUEUE_SERIAL), ^{
_cycleDo(block);
finish = finish | 0x010;
});
_cycleDo(block);
finish = finish | 0x100;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10*NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
NSLog(@"finish:%x", finish);
if(last) {last();}
});
}
NSLock
一个互斥锁,不能被递归调用,性能优秀
lock & unlock
同一个对象,不加锁的多线程访问全局静态变量
// LockObj
- (void)unlockNum2{
_kNum2+=1;
}
void lockCommonTest2() {
LockObj *obj = [[LockObj alloc] init];
_asyncDo(^{
[obj unlockNum2];
}, ^{
NSLog(@"num2:%d", LockObj.num2);
});
}
- 日志输出
// 1
Hello, World!
finish:111
num2:9000
// 2
Hello, World!
finish:111
num2:8331
- 结论
- 多线程对全局静态变量的修改不能保证正确性
同一个对象,锁使用对象的lock访问全局静态变量
// LockObj
- (void)lockNum3 {
[self.lock lock];
self.num3+=1;
[self.lock unlock];
}
void lockNum3ParamLock() {
LockObj *obj = [[LockObj alloc] init];
_asyncDo(^{
[obj lockNum3];
}, ^{
NSLog(@"num3:%d", obj.num3);
});
}
- 日志输出
// 多次输出相同
Hello, World!
finish:111
num3:9000
- 结论
- 对象的属性的锁,成功多对象的其他属性就行锁,保证了其他属性的正确性
多个对象,锁使用对象的lock访问属性变量
// LockObj
- (void)unlockNum3 {
self.num3+=1;
}
void lockMulpNum3ParamLock() {
LockObj *obj1 = [[LockObj alloc] init];
LockObj *obj2 = [[LockObj alloc] init];
LockObj *obj3 = [[LockObj alloc] init];
_asyncDo(^{
[obj1 lockNum3];
[obj2 unlockNum3];
[obj3 lockNum3];
}, ^{
NSLog(@"num3:%d, num3:%d, num3:%d", obj1.num3, obj2.num3, obj3.num3);
});
}
- 日志输出
Hello, World!
finish:111
num3:9000, num3:8963, num3:9000
结论
-
obj1
和obj3
的锁没有影响到obj2
的属性修改,obj2
属性的num3
修改不能保证正确性
多个对象,锁使用对象的lock访问全局静态变量
// LockObj
- (void)lockNum1{
[self.lock lock];
_kNum1+=1;
[self.lock unlock];
}
void lockMulpNum1ParamLock() {
LockObj *obj1 = [[LockObj alloc] init];
LockObj *obj2 = [[LockObj alloc] init];
LockObj *obj3 = [[LockObj alloc] init];
_asyncDo(^{
[obj1 lockNum1];
[obj2 lockNum1];
[obj3 lockNum1];
}, ^{
NSLog(@"num1:%d", LockObj.num1);
});
}
- 日志输出
Hello, World!
finish:111
num1:26566
结论
- 多个
NSLock
锁之间并不会互斥,既obj1
的锁只会影响obj1
对_kNum1
的操作,不影响obj2
,ob3
对_kNum1
的操作
多个对象使用全局锁访问全局静态变量
// LockObj
- (void)klockNum1 {
[_klock lock];
_kNum1+=1;
[_klock unlock];
}
void lockMulpNum1kLock() {
LockObj *obj1 = [[LockObj alloc] init];
LockObj *obj2 = [[LockObj alloc] init];
LockObj *obj3 = [[LockObj alloc] init];
_asyncDo(^{
[obj1 klockNum1];
[obj2 klockNum1];
[obj3 klockNum1];
}, ^{
NSLog(@"num1:%d", LockObj.num1);
});
}
- 日志输出
// 多次输出相同
Hello, World!
finish:111
num1:27000
- 结论
- 全局静态变量的多线程访问,需要使用全局的
NSLock
锁才能保证操作的正确性
- 全局静态变量的多线程访问,需要使用全局的
临界区
// LockObj
- (void)klockNum124{
_kNum1+=1;
[_klock lock];
_kNum2+=1;
[_klock unlock];
_kNum4+=1;
}
- 日志输出
Hello, World!
finish:111
num1:26910, num2:27000, num4:26949
Hello, World!
finish:111
num1:26910, num2:27000, num4:26825
Hello, World!
finish:111
num1:26902, num2:27000, num4:26924
结论
- 只有在
lock
和unlock
之间的代码才会被限制多线程访问
tryLock
tryLock 会加锁,在没有获得锁的时候可以做其他事。
// LockObj
- (void)trylockNum12{
if ([_klock tryLock]) {
_kNum1+=1;
[_klock unlock];
}else {
[self.lock lock];
_kNum2+=1;
[self.lock lock];
}
}
void lockNum123trylock() {
LockObj *obj = [[LockObj alloc] init];
_asyncDo(^{
[obj trylockNum12];
}, ^{
NSLog(@"num1:%d, num2:%d", LockObj.num1, LockObj.num2);
});
}
- 日志输出
Hello, World!
finish:111
num1:7700, num2:1300
- 结论
- 👍
@synchronized
@synchronized
长得挺别致,到底是个什么东西??
@synchronized (<#token#>) {
<#statements#>
}
两个参数,token
更像是一种标识,并且在测试过程中,输出的日志也确实符合这个规律。不考虑在递归调用上的问题,更像是这种
static NSMutableDictionary *dic;
NSLock *lock = [dic objectForKey:token];
if (lock) {
[lock lock];
//do something
[lock unlock];
}
仿照之前的测试代码,开始准备测试
token使用一个self来对自身属性做改变
// LockObj
- (void)syncLockNum3 {
@synchronized (self) {
self.num3+=1;
}
}
void syncLockMulpNum3ParamLock (void) {
LockObj *obj1 = [[LockObj alloc] init];
LockObj *obj2 = [[LockObj alloc] init];
LockObj *obj3 = [[LockObj alloc] init];
_asyncDo(^{
[obj1 syncLockNum3];
[obj2 unlockNum3];
[obj3 syncLockNum3];
}, ^{
NSLog(@"num3:%d, num3:%d, num3:%d", obj1.num3, obj2.num3, obj3.num3);
});
}
- 日志输出
Hello, World!
finish:111
num3:9000, num3:8980, num3:9000
Hello, World!
finish:111
num3:9000, num3:8974, num3:9000
- 结论
-
@synchronized
很好的避免了多线程对num3
修改
-
token使用一个self来对全局静态变量做改变
// LockObj
- (void)syncLockNum1 {
@synchronized (self) {
_kNum1 +=1;
}
}
void syncLockMulpNum1ParamLock () {
LockObj *obj1 = [[LockObj alloc] init];
LockObj *obj2 = [[LockObj alloc] init];
LockObj *obj3 = [[LockObj alloc] init];
_asyncDo(^{
[obj1 syncLockNum1];
[obj2 syncLockNum1];
[obj3 syncLockNum1];
}, ^{
NSLog(@"num1:%d", LockObj.num1);
});
}
- 日志输出
Hello, World!
finish:111
num1:25618
- 结论
-
@synchronized
并没有防止多线程对_kNum1
修改
-
token使用一个字符串来对全局静态变量做改变
// LockObj
- (void)ksyncLockNum124 {
@synchronized (@"num1") {
_kNum1+=1;
}
@synchronized (@"num2") {
_kNum2+=1;
}
_kNum4 += 1;
}
void syncLockMulpNum124Lock () {
LockObj *obj1 = [[LockObj alloc] init];
LockObj *obj2 = [[LockObj alloc] init];
LockObj *obj3 = [[LockObj alloc] init];
_asyncDo(^{
[obj1 ksyncLockNum124];
[obj2 ksyncLockNum124];
[obj3 ksyncLockNum124];
}, ^{
NSLog(@"num1:%d, num2:%d, num4:%d", LockObj.num1, LockObj.num2, LockObj.num4);
});
}
- 日志输出
Hello, World!
finish:111
num1:27000, num2:27000, num4:26738
Hello, World!
finish:111
num1:27000, num2:27000, num4:26724
-
@synchronized
的字符串token
很好的防止多线程对_kNum1
、_kNum2
修改
递归锁
不加锁
// LockObj
- (void)arrAdd{
[self.array addObject:[NSString stringWithFormat:@"%d", arc4random()%1000000]];
}
- (void)arrEnum{
for (NSString *str in self.array) {
[str stringByAppendingString:@"***"];
}
}
void lockArrayUnlock() {
LockObj *obj = [[LockObj alloc] init];
[obj arrAdd];
_asyncDo(^{
[obj arrAdd];
[obj arrEnum];
}, nil);
}
- 日志输出
*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSArrayM: 0x100606410> was mutated while being enumerated.'
加锁
// LockObj
- (void)syncLockArrAdd{
@synchronized (self) {
[self.array addObject:[NSString stringWithFormat:@"%d", arc4random()%1000000]];
}
}
- (void)syncLockArrEnum{
@synchronized (self) {
for (NSString *str in self.array) {
[str stringByAppendingString:@"***"];
}
}
}
void lockArraySyncRecLock(void) {
LockObj *obj = [[LockObj alloc] init];
[obj arrAdd];
_asyncDo(^{
[obj syncLockArrAdd];
[obj syncLockArrEnum];
}, nil);
}
- 日志输出
Hello, World!
finish:111
Hello, World!
finish:110
- 结论
可变数组,如果涉及到多线程操作,加锁