天天看點

不同鎖的性能比較

1、OSSpinLock(自旋鎖) High-level lock

OSSpinLock叫做”自旋鎖”,等待鎖的線程會處于忙等(busy-wait)狀态,一直占用着CPU資源

目前已經不再安全,可能會出現優先級反轉問題

如果等待鎖的線程優先級較高,它會一直占用着CPU資源,優先級低的線程就無法釋放鎖

需要導入頭檔案#import <libkern/OSAtomic.h>

#import <libkern/OSAtomic.h>

@interface OSSpinLockDemo()
@property (assign, nonatomic) OSSpinLock lock;
@end

@implementation OSSpinLockDemo

- (void)viewDidLoad {
    [super viewDidLoad];

     // 初始化鎖
    self.lock = OS_SPINLOCK_INIT;

    //加鎖
    OSSpinLockLock(&_lock);
    
     //嘗試加鎖,如果不需要等待就加鎖傳回ture,如果需要等待就不加鎖傳回false
    //bool result = OSSpinLockTry(&_lock);

    [self testSpinLock];

    //解鎖
    OSSpinLockUnlock(&_lock);
}
           

2、os_unfair_lock(互斥鎖) Low-level lock

os_unfair_lock用于取代不安全的OSSpinLock ,從iOS10開始才支援

從底層調用看,等待os_unfair_lock鎖的線程會處于休眠狀态,并非忙等

需要導入頭檔案#import <os/lock.h>

#import <os/lock.h>

@interface OSUnfairLockDemo()
@property (assign, nonatomic) os_unfair_lock lock;
@end

@implementation OSUnfairLockDemo

- (void)viewDidLoad {
    [super viewDidLoad];

     // 初始化鎖
    self.lock = OS_UNFAIR_LOCK_INIT;

    //加鎖
    os_unfair_lock_lock(&_lock);
    
     //嘗試加鎖,如果不需要等待就加鎖傳回ture,如果需要等待就不加鎖傳回false
    //bool result = os_unfair_lock_trylock(&_lock);

    [self testUnfairLock];

    //解鎖
    os_unfair_lock_unlock(&_lock);
}
           

 ibireme 大神 《不再安全的 OSSpinLock》 這篇文章表示OSSpinLock不再安全,新版 iOS 中,系統維護了 5 個不同的線程優先級/QoS: background,utility,default,user-initiated,user-interactive。高優先級線程始終會在低優先級線程前執行,一個線程不會受到比它更低優先級線程的幹擾。這種線程排程算法會産生潛在的優先級反轉問題,進而破壞了 spinlock。

3、pthread_mutex -需要銷毀

#import <pthread.h>

/*

/*
 * Mutex type attributes
 */
#define PTHREAD_MUTEX_NORMAL		0
#define PTHREAD_MUTEX_ERRORCHECK	1
#define PTHREAD_MUTEX_RECURSIVE		2
#define PTHREAD_MUTEX_DEFAULT		PTHREAD_MUTEX_NORMAL


*/
@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t mutex;
@end

@implementation MutexDemo

- (void)viewDidLoad {
    [super viewDidLoad];

    // 靜态初始化
    //pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    // 初始化屬性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    // 初始化鎖
    pthread_mutex_init(_mutex, &attr);

    //pthread_mutex_init(_mutex, NULL);//傳空也是預設PTHREAD_MUTEX_DEFAULT

    // 銷毀屬性
    pthread_mutexattr_destroy(&attr);

    pthread_mutex_lock(&_mutex);
    
    [self testMutex];
    
    pthread_mutex_unlock(&_mutex);
}

- (void)testMutex
{
    NSLog(@"%s", __func__);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
}
           
#import <pthread.h>

@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t mutex;
@end

@implementation MutexDemo

- (void)viewDidLoad {
    [super viewDidLoad];

    // 初始化屬性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    // 遞歸鎖:允許同一個線程對一把鎖進行重複加鎖
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    // 初始化鎖
    pthread_mutex_init(_mutex, &attr);

    // 銷毀屬性
    pthread_mutexattr_destroy(&attr);
 
    [self otherTest];
}

- (void)testMutex
{
    NSLog(@"%s", __func__);
}

- (void)otherTest
{
    pthread_mutex_lock(&_mutex);
    
    NSLog(@"%s", __func__);
    
    static int count = 0;
    if (count < 10) {
        count++;
        [self otherTest];
    }
    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
}
           
#import <pthread.h>

@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation MutexDemo

- (void)viewDidLoad {
    [super viewDidLoad];
    // 初始化屬性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);//條件鎖
    // 初始化鎖
    pthread_mutex_init(&_mutex, &attr);
    // 銷毀屬性
    pthread_mutexattr_destroy(&attr);
        
    // 初始化條件
    pthread_cond_init(&_cond, NULL);
        
    self.data = [NSMutableArray array];
    [self otherTest];
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}

// 生産者-消費者模式

// 線程1
// 删除數組中的元素
- (void)__remove
{
    pthread_mutex_lock(&_mutex);
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待
        pthread_cond_wait(&_cond, &_mutex);
    }
    
    [self.data removeLastObject];
    NSLog(@"删除了元素");
    
    pthread_mutex_unlock(&_mutex);
}

// 線程2
// 往數組中添加元素
- (void)__add
{
    pthread_mutex_lock(&_mutex);
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    
    // 信号
    pthread_cond_signal(&_cond);
    // 廣播
//    pthread_cond_broadcast(&_cond);
    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
}
           

4、NSLock

NSLock是對mutex普通鎖的封裝

不同鎖的性能比較

5、NSRecursiveLock

NSRecursiveLock也是對mutex遞歸鎖的封裝,API跟NSLock基本一緻

6、NSCondition

NSCondition是對mutex和cond的封裝

@interface NSConditionDemo()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation NSConditionDemo

- (void)viewDidLoad {
    [super viewDidLoad];

     self.condition = [[NSCondition alloc] init];
     self.data = [NSMutableArray array];
    [self otherTest];
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}


// 線程1
// 删除數組中的元素
- (void)__remove
{
    [self.condition lock];
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待
        [self.condition wait];
    }
    
    [self.data removeLastObject];
    NSLog(@"删除了元素");
    
    [self.condition unlock];
}

// 線程2
// 往數組中添加元素
- (void)__add
{
    [self.condition lock];
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    
    // 信号
    [self.condition signal];
    
    sleep(2);
    
    [self.condition unlock];
}
           

7、NSConditionLock

NSConditionLock是對NSCondition的進一步封裝,可以設定具體的條件值,預設為0;

@interface NSConditionLockDemo()
@property (strong, nonatomic) NSConditionLock *conditionLock;
@end

@implementation NSConditionLockDemo

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];

    [self otherTest];
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}

- (void)__one
{
    [self.conditionLock lock];//不管條件值,直接加鎖
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two
{
    [self.conditionLock lockWhenCondition:2];//必須滿足條件才能加鎖
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}

- (void)__three
{
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}
           

8、dispatch_semaphore

semaphore叫做”信号量”

信号量的初始值,可以用來控制線程并發通路的最大數量

信号量的初始值為1,代表同時隻允許1條線程通路資源,保證線程同步

@interface SemaphoreDemo()
@property (strong, nonatomic) dispatch_semaphore_t semaphore;
@end

@implementation SemaphoreDemo

- (void)viewDidLoad {
    [super viewDidLoad];
     self.semaphore = dispatch_semaphore_create(5);//設定為1則是串行隊列,>1為并發
}

- (void)otherTest
{
    for (int i = 0; i < 20; i++) {
        [[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start];
    }
}

- (void)test
{
    // 如果信号量的值 > 0,就讓信号量的值減1,然後繼續往下執行代碼
    // 如果信号量的值 <= 0,就會休眠等待,直到信号量的值變成>0,就讓信号量的值減1,然後繼續往下執行代碼
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    
    sleep(2);
    NSLog(@"test - %@", [NSThread currentThread]);
    
    // 讓信号量的值+1
    dispatch_semaphore_signal(self.semaphore);
}
           

9、synchronized(遞歸鎖)

@synchronized是對mutex遞歸鎖的封裝,在xcode編寫的時候不會有提示,性能比較低。

@synchronized(obj)内部會生成obj對應的遞歸鎖,然後進行加鎖、解鎖操作

/*
typedef struct alignas(CacheLineSize) SyncData {
    struct SyncData* nextData;
    DisguisedPtr<objc_object> object;
    int32_t threadCount;  // number of THREADS using this block
    recursive_mutex_t mutex;
} SyncData;

*/
//底層實作 objc_sync.mm  objc_sync_enter objc_sync_exit  syncData  裡面是哈希表查找 c++結構
@synchronized([self class]) {
 
     [self otherTest];
}
           

性能從高到低排序:os_unfair_lock >OSSpinLock>dispatch_semaphore>pthread_mutex>NSLock>NSCondition>pthread_mutex(recursive)>NSConditionLock>@synchronized(在加鎖和解鎖的代碼上算出所用的時間差)OSSpinLock,dispatch_semaphore的性能遠遠優于其他的鎖,但是OSSpinLock由于優先級反轉的問題,蘋果在iOS10的時候推出了os_unfair_lock來替代,而且性能不減當年,但是要在iOS10之後才能用(雖然自旋鎖的性能優于互斥鎖),可以看出@synchronize和NSConditionLock明顯性能最差,如果項目中對性能特别敏感,建議使用dispatch_semaphore,如果基于友善的話就用@synchronize也可以,影響不大。

繼續閱讀