版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2017.05.21 |
前言
ios中有好幾種鎖,比如自旋鎖,互斥鎖,信號量等等,鎖其實是多線程數據安全的一種解決方案,作用就是保證同一時間只有一個線程訪問和改變某些敏感數據,這些鎖的性能也是差別很大,最近看了幾個技術大牛的技術博客,我才發現我以前對鎖的理解太膚淺了,心虛的趕緊找資料又開始了深入學習,然后整理出來。前面介紹了幾種鎖:
1. ios開發中的幾種鎖(一)
這篇接著講其他的幾種鎖。
詳情
一、pthread_mutex 互斥鎖
ibireme在《不再安全的OSSpinLock》這篇文章中提到性能最好的OSSpinLock不再是線程安全的并把自己開源項目中的OSSpinLock都替換成了pthread_mutex。下面還是先看代碼。
1. JJPtheadVC.h
#import <UIKit/UIKit.h>
@interface JJPtheadVC : UIViewController
@end
2. JJPtheadVC.m
#import "JJPtheadVC.h"
#import <pthread.h>
@interface JJPtheadVC ()
@end
@implementation JJPtheadVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
//互斥所
[self aboutPthreadMutex];
}
#pragma mark - Object Private Function
- (void)aboutPthreadMutex
{
static pthread_mutex_t mutexLock;
pthread_mutex_init(&mutexLock, NULL);
NSInteger __block num = 10;
//線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"線程1準備上鎖,%@",[NSThread currentThread]);
pthread_mutex_lock(&mutexLock);
num = num + 1;
NSLog(@"線程1--num1=%ld",num);
pthread_mutex_unlock(&mutexLock);
});
//線程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"線程2準備上鎖,%@",[NSThread currentThread]);
pthread_mutex_lock(&mutexLock);
num = num + 1;
NSLog(@"線程2--num2=%ld",num);
pthread_mutex_unlock(&mutexLock);
});
}
@end
查看結果
2017-05-20 13:27:14.675 lock[3995:185687] 線程2準備上鎖,<NSThread: 0x60800007e0c0>{number = 4, name = (null)}
2017-05-20 13:27:14.675 lock[3995:185685] 線程1準備上鎖,<NSThread: 0x600000265080>{number = 3, name = (null)}
2017-05-20 13:27:14.675 lock[3995:185687] 線程2--num2=11
2017-05-20 13:27:14.678 lock[3995:185685] 線程1--num1=12
注意:pthread_mutex 中也有個pthread_mutex_trylock(&pLock),和上面提到的 OSSpinLockTry(&oslock)區別在于,前者可以加鎖時返回的是 0,否則返回一個錯誤提示碼;后者返回的 YES和NO。
二、pthread_mutex(recursive) 遞歸鎖
根據上面講述的幾個鎖,可以發現:加鎖后只能有一個線程訪問該對象,后面的線程需要排隊,并且lock與unlock都是對應出現的,同一線程多次lock是不允許的,而遞歸鎖允許同一個線程在未釋放其擁有的鎖時反復對該鎖進行加鎖操作。
下面直接看代碼
1. JJPthreadRecursiveVC.h
#import <UIKit/UIKit.h>
@interface JJPthreadRecursiveVC : UIViewController
@end
2. JJPthreadRecursiveVC.m
#import "JJPthreadRecursiveVC.h"
#import <pthread.h>
@interface JJPthreadRecursiveVC ()
@end
@implementation JJPthreadRecursiveVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
//遞歸鎖
[self aboutPthreadMutexRecursive];
}
#pragma mark - Object Private Function
- (void)aboutPthreadMutexRecursive
{
static pthread_mutex_t pLock;
pthread_mutexattr_t attr;
//初始化attr并且給它賦予默認
pthread_mutexattr_init(&attr);
//設置鎖類型,這邊是設置為遞歸鎖
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&pLock, &attr);
//銷毀一個屬性對象,在重新進行初始化之前該結構不能重新使用
pthread_mutexattr_destroy(&attr);
//1.線程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveBlock)(int);
RecursiveBlock = ^(int value) {
pthread_mutex_lock(&pLock);
if (value > 0) {
NSLog(@"value = %d", value);
NSLog(@"線程 = ,%@",[NSThread currentThread]);
RecursiveBlock(value - 1);
}
pthread_mutex_unlock(&pLock);
};
RecursiveBlock(5);
});
}
@end
直接看輸出結果
2017-05-21 17:29:14.341 lock[1271:55126] value = 5
2017-05-21 17:29:14.342 lock[1271:55126] 線程 = ,<NSThread: 0x60800006e440>{number = 3, name = (null)}
2017-05-21 17:29:14.342 lock[1271:55126] value = 4
2017-05-21 17:29:14.343 lock[1271:55126] 線程 = ,<NSThread: 0x60800006e440>{number = 3, name = (null)}
2017-05-21 17:29:14.343 lock[1271:55126] value = 3
2017-05-21 17:29:14.344 lock[1271:55126] 線程 = ,<NSThread: 0x60800006e440>{number = 3, name = (null)}
2017-05-21 17:29:14.344 lock[1271:55126] value = 2
2017-05-21 17:29:14.345 lock[1271:55126] 線程 = ,<NSThread: 0x60800006e440>{number = 3, name = (null)}
2017-05-21 17:29:14.348 lock[1271:55126] value = 1
2017-05-21 17:29:14.349 lock[1271:55126] 線程 = ,<NSThread: 0x60800006e440>{number = 3, name = (null)}
注意:上面的代碼如果我們用 pthread_mutex_init(&pLock, NULL) 初始化會出現死鎖的情況,遞歸鎖能很好的避免這種情況的死鎖。
相關參考技術博客
1.iOS 開發中的八種鎖(Lock)
2.不再安全的 OSSpinLock
3. NSRecursiveLock遞歸鎖的使用
4.關于dispatch_semaphore的使用
5.實現鎖的多種方式和鎖的高級用法
后記
剩下的幾種鎖,待續哦~~~~
思念