iOS开发进阶:锁的分析

我们在使用多线程的时候,同一时刻可能会有多个线程访问同一内存的内容,这样就很容易引发数据混乱(数据安全)的问题。为了减少或者避免这种问题的出现,我们需要使用锁来保证统一时刻只有一个线程访问这一块内存。锁可以让数据的访问更安全。

我们常见的锁包括OSSpinLockdispatch_semaphore_tos_unfair_lockpthread_mutex_tNSlockNSConditionpthread_mutex_t(recursive)NSRecursiveLockNSConditionLock@synchronized等10来种。

按照特性划分,锁可以分为自旋锁互斥锁:
1.自旋锁:线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,甚至显示释放自旋锁。自旋锁避免了进程上下文调度的开销,因此对于线程只会阻塞很短时间的场合是有效的。
2.互斥锁:是一种利用于多线程编程中,防止两条线程同时对统一资源进行读写的机制。该目的是通过将代码切片成一个一个临时区二达成的。属于互斥锁的有NSlockpthread_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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章