NSDictionary是线程安全的,NSMutableDictionary是线程不安全的。
利用锁来保证线程的安全。
代码地址,欢迎star
测试方式:
- 对NSMutableDictionary进行100000次set和get操作。
- 设备为iPhone 6
现在有几种方式可以保证线程安全
- @synchronized
- NSLock
- OSSpinLock
- GCD semaphore
- GCD serial queue
- pthread mutex
在子线程串行执行结果:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
double then, now;
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_syncDictionary setObject:@(i) forKey:@(i)];
[_syncDictionary objectForKey:@(i)];
}
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.synchronizedLabel.text = [NSString stringWithFormat:@"@synchronized: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_nsLockDictionary setObject:@(i) forKey:@(i)];
[_nsLockDictionary objectForKey:@(i)];
}
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.nsLockLabel.text = [NSString stringWithFormat:@"NSLock: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_spinLockDictionary setObject:@(i) forKey:@(i)];
[_spinLockDictionary objectForKey:@(i)];
}
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.osSpinLockLabel.text = [NSString stringWithFormat:@"OSSpinLock: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_semaphoreDictionary setObject:@(i) forKey:@(i)];
[_semaphoreDictionary objectForKey:@(i)];
}
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.gcdSemaphoreLabel.text = [NSString stringWithFormat:@"GCD Semaphore: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_queueDictionary setObject:@(i) forKey:@(i)];
[_queueDictionary objectForKey:@(i)];
}
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.gcdQueueLabel.text = [NSString stringWithFormat:@"GCD Queue: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_ptMutexDictionary setObject:@(i) forKey:@(i)];
[_ptMutexDictionary objectForKey:@(i)];
}
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.pthreadMutexLabel.text = [NSString stringWithFormat:@"PThread mutex: %f sec\n", now-then];
self.twoBlockTestButton.enabled = YES;
self.lockUnlockTestButton.enabled = YES;
});
});
image.png
线程安全的NSMutableDictionary,在两个并发的block中执行100000次set,get
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
double then, now;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
then = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_syncDictionary setObject:@(i) forKey:@(i)];
[_syncDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_syncDictionary setObject:@(i) forKey:@(i)];
[_syncDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.synchronizedLabel.text = [NSString stringWithFormat:@"@synchronized: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_nsLockDictionary setObject:@(i) forKey:@(i)];
[_nsLockDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_nsLockDictionary setObject:@(i) forKey:@(i)];
[_nsLockDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.nsLockLabel.text = [NSString stringWithFormat:@"NSLock: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_spinLockDictionary setObject:@(i) forKey:@(i)];
[_spinLockDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_spinLockDictionary setObject:@(i) forKey:@(i)];
[_spinLockDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.osSpinLockLabel.text = [NSString stringWithFormat:@"OSSpinLock: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_semaphoreDictionary setObject:@(i) forKey:@(i)];
[_semaphoreDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_semaphoreDictionary setObject:@(i) forKey:@(i)];
[_semaphoreDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.gcdSemaphoreLabel.text = [NSString stringWithFormat:@"GCD Semaphore: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_queueDictionary setObject:@(i) forKey:@(i)];
[_queueDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_queueDictionary setObject:@(i) forKey:@(i)];
[_queueDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.gcdQueueLabel.text = [NSString stringWithFormat:@"GCD Queue: %f sec\n", now-then];
});
then = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_ptMutexDictionary setObject:@(i) forKey:@(i)];
[_ptMutexDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSUInteger i = 0; i < _iterationsCount; i++)
{
[_ptMutexDictionary setObject:@(i) forKey:@(i)];
[_ptMutexDictionary objectForKey:@(i)];
}
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
now = CFAbsoluteTimeGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
self.pthreadMutexLabel.text = [NSString stringWithFormat:@"PThread mutex: %f sec\n", now-then];
self.twoBlockTestButton.enabled = YES;
self.lockUnlockTestButton.enabled = YES;
});
});
image.png
在并发的线程安全的NSMutableDictionary,性能最好的是OSSpinLock。
自旋锁的优点:
- 自旋锁,和互斥锁类似,都是为了保证线程安全的锁。但二者的区别是不一样的,对于互斥锁,当一个线程获得这个锁之后,其他想要获得此锁的线程将会被阻塞,直到该锁被释放。
- 但自选锁不一样,当一个线程获得锁之后,其他线程将会一直循环在哪里查看是否该锁被释放。所以,此锁比较适用于锁的持有者保存时间较短的情况下。
但是OSSpinLock不安全。
-
新版 iOS 中,系统维护了 5 个不同的线程优先级/QoS: background,utility,default,user-initiated,user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比它更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级反转问题,从而破坏了 spin lock。
-
具体来说,如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。这并不只是理论上的问题,libobjc 已经遇到了很多次这个问题了,于是苹果的工程师停用了 OSSpinLock。
os_unfair_lock
自旋锁已经不在安全,然后苹果又整出来个 os_unfair_lock_t
这个锁解决了优先级反转问题。
os_unfair_lock_t unfairLock;
unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock);
os_unfair_lock_unlock(unfairLock);
结论
所以如果同一线程优先级可以用,OSSpinLock。如果不在同一线程优先级的话在iOS 10用os_unfair_lock
网友评论