我們在使用多線程的時候,同一時刻可能會有多個線程訪問同一內存的內容,這樣就很容易引發數據混亂(數據安全)的問題。爲了減少或者避免這種問題的出現,我們需要使用鎖來保證統一時刻只有一個線程訪問這一塊內存。鎖可以讓數據的訪問更安全。
我們常見的鎖包括OSSpinLock
、dispatch_semaphore_t
、os_unfair_lock
、pthread_mutex_t
、NSlock
、NSCondition
、pthread_mutex_t(recursive)
、NSRecursiveLock
、NSConditionLock
、@synchronized
等10來種。
按照特性劃分,鎖可以分爲自旋鎖
和互斥鎖
:
1.自旋鎖:線程反覆檢查鎖變量是否可用。由於線程在這一過程中保持執行,因此是一種忙等待。一旦獲取了自旋鎖,線程會一直保持該鎖,甚至顯示釋放自旋鎖。自旋鎖避免了進程上下文調度的開銷,因此對於線程只會阻塞很短時間的場合是有效的。
2.互斥鎖:是一種利用於多線程編程中,防止兩條線程同時對統一資源進行讀寫的機制。該目的是通過將代碼切片成一個一個臨時區二達成的。屬於互斥鎖的有NSlock
、pthread_mutex_t
、@synchronized
。
那麼它們各自的性能優勢什麼樣的呢?我們來做一個簡單的測試,測試代碼如下:
- (void)test{
int loop = 100000;
{
OSSpinLock lock = OS_SPINLOCK_INIT;
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
OSSpinLockLock(&lock);
OSSpinLockUnlock(&lock);
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"OSSpinLock:%f", (end - start)*1000);
}
{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(semaphore);
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"dispatch_semaphore_t:%f", (end - start)*1000);
}
{
os_unfair_lock unfair = OS_UNFAIR_LOCK_INIT;
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
os_unfair_lock_lock(&unfair);
os_unfair_lock_unlock(&unfair);
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"os_unfair_lock:%f", (end - start)*1000);
}
{
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
pthread_mutex_lock(&mutex);
pthread_mutex_unlock(&mutex);
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"pthread_mutex_t:%f", (end - start)*1000);
}
{
pthread_mutex_t recurive;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&recurive, &attr);
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
pthread_mutex_lock(&recurive);
pthread_mutex_unlock(&recurive);
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"pthread_mutex_t(recurive):%f", (end - start)*1000);
}
{
NSLock *lock = [NSLock new];
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
[lock lock];
[lock unlock];
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"NSLock:%f", (end - start)*1000);
}
{
NSRecursiveLock *recursiveLock = [NSRecursiveLock new];
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
[recursiveLock lock];
[recursiveLock unlock];
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"NSRecursiveLock:%f", (end - start)*1000);
}
{
NSCondition *condition = [NSCondition new];
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
[condition lock];
[condition unlock];
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"NSCondition:%f", (end - start)*1000);
}
{
NSConditionLock *conditionLock = [NSConditionLock new];
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
[conditionLock lock];
[conditionLock unlock];
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"NSConditionLock:%f", (end - start)*1000);
}
{
double_t start = CFAbsoluteTimeGetCurrent();
for (int i = 0; i < loop; i++){
@synchronized (self) {
}
}
double_t end = CFAbsoluteTimeGetCurrent();
NSLog(@"@synchronized:%f", (end - start)*1000);
}
}
也就是我們嘗試加鎖解鎖10萬次,看這個過程耗費的時間(單位:毫秒ms)。
在模擬器上運行時間如下:(單位:毫秒)
在真機上運行時間如下:(單位:毫秒)
真機和模擬器上運行結果基本相近,在真機上@synchronized的表現要比模擬器上號很多,從7.5毫秒提升到2.7毫秒(猜測:@synchronized可能蘋果針對arm有專門的優化?)
1.@synchronized
加鎖的效果
遞歸可重用
結構
const NSString *syncKey = (NSString *)&__NSConstantStringImpl__var_folders_ns_73tnh7591jvg16yqm8fn6ykh0000gn_T_NXXcrun_0a2d9a_mi_0;
// @implementation NXXcrun
static void _C_NXXcrun_main(Class self, SEL _cmd) {
{
id _rethrow = 0;
id _sync_obj = (id)syncKey;
objc_sync_enter(_sync_obj);
try {
struct _SYNC_EXIT {
_SYNC_EXIT(id arg) : sync_exit(arg) {}
~_SYNC_EXIT() {objc_sync_exit(sync_exit);}
id sync_exit;
}
_sync_exit(_sync_obj);
} catch (id e) {_rethrow = e;}
{ struct _FIN {
_FIN(id reth) : rethrow(reth) {}
~_FIN() { if (rethrow) objc_exception_throw(rethrow); }
id rethrow;
} _fin_force_rethow(_rethrow);}
}
}
// @end