美文网首页
iOS线程同步(各种锁)

iOS线程同步(各种锁)

作者: 乔克蜀黍 | 来源:发表于2021-11-12 16:31 被阅读0次
    线程安全

    在iOS开发中经常会遇到一块资源被多个线程共享的情况,也就是多个线程会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题


    数据错乱.png
    解决方案

    使用线程同步技术(同步就是协同步调,按预定的先后顺序进行),常见的同步技术时加锁


    加锁.png
    OSSpinLock

    OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源,目前已经不再安全,从iOS10已经废弃了,可能会出现优先级反转问题,如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁,使用需要导入头文件#import <libkern/OSAtomic.h>。

    //初始化
        OSSpinLock lock = OS_SPINLOCK_INIT;
        //尝试加锁
        bool result = OSSpinLockTry(&lock);
        //加锁
        OSSpinLockLock(&lock);
        //解锁
        OSSpinLockUnlock(&lock);
    
    os_unfair_lock

    os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持,从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等,使用需要导入头文件#import <os/lock.h>。

    //初始化
    os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
    //尝试加锁
    os_unfair_lock_trylock(&lock);
    //加锁
    os_unfair_lock_lock(&lock);
    //解锁
    os_unfair_lock_unlock(&lock);
    
    pthread_mutex

    mutex叫做”互斥锁”,等待锁的线程会处于休眠状态,使用需要导入头文件#import <pthread.h>

    // 初始化属性
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    // 初始化锁
    pthread_mutex_t mutex
    pthread_mutex_init(mutex, &attr);
    //尝试加锁
    pthread_mutex_trylock(mutex);
    //加锁
    pthread_mutex_lock(&mutex);
    //解锁
    pthread_mutex_unlock(&mutex);
    // 销毁属性
    pthread_mutexattr_destroy(&attr);
    
    /*
     * 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
    
    NSLock、NSRecursiveLock

    NSLock、NSRecursiveLock是对mutex普通锁的封装,NSLock遵守NSLocking协议

    @protocol NSLocking
    
    - (void)lock;
    - (void)unlock;
    
    @end
    
    @interface NSLock : NSObject <NSLocking> {
    - (BOOL)tryLock;
    - (BOOL)lockBeforeDate:(NSDate *)limit;
    @end
    

    初始化锁NSLock *lock = [[NSLock alloc] init];
    NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致

    NSCondition

    NSCondition是对mutex和cond的封装,NSCondition遵守NSLocking协议

    @interface NSCondition : NSObject <NSLocking> {
    
    - (void)wait;
    - (BOOL)waitUntilDate:(NSDate *)limit;
    - (void)signal; //通知
    - (void)broadcast; //广播
    
    @end
    

    NSCondition通常用于生产者消费者模式的业务中,当不满足条件时调用wait方法让消费者线程等待,当条件满足时调用signal方法通知消费者线程。

    // 生产者-消费者模式
    
    // 线程1
    // 删除数组中的元素
    - (void)__remove
    {
        [self.condition lock];
        if (self.data.count == 0) {
            // 等待
            [self.condition wait];
        }
        [self.data removeLastObject];
        [self.condition unlock];
    }
    
    // 线程2
    // 往数组中添加元素
    - (void)__add
    {
        [self.condition lock];
        [self.data addObject:@"Test"];
        // 信号
        [self.condition signal];
        // 广播
        //[self.condition broadcast];
        [self.condition unlock];
    }
    
    NSConditionLock

    NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值

    @interface NSConditionLock : NSObject <NSLocking> {
    
    @property (readonly) NSInteger condition;
    
    - (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
    
    - (void)lockWhenCondition:(NSInteger)condition;
    - (BOOL)tryLock;
    - (BOOL)tryLockWhenCondition:(NSInteger)condition;
    - (void)unlockWithCondition:(NSInteger)condition;
    - (BOOL)lockBeforeDate:(NSDate *)limit;
    - (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
    
    @end
    

    NSConditionLock都可以设置不同线程间的依赖,让满足条件值的线程先执行,不满足条件的线程处于等待状态。

    dispatch_semaphore

    semaphore叫做”信号量”,信号量的初始值,可以用来控制线程并发访问的最大数量,信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步

    //信号量的初始值
    int value = 1;
    //初始化信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);
    //如果信号量的值<=0,当前线程就会进入休眠等待(知道信号量的值>0)
    //如果信号量的值>0,就减1,然后往下执行后面的代码
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    //让信号量的值加1
    dispatch_semaphore_signal(semaphore);
    
    @synchronized

    @synchronized是对mutex递归锁的封装,源码查看:objc4中的objc-sync.mm文件,@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作

    @synchronized (obj) {
        //任务
    }
    
    iOS线程同步方案性能比较

    性能从高到底排序

    • os_unfair_lock
    • OSSpinLock
    • dispatch_semaphore
    • pthread_mutex
    • dispatch_queue(DISPATCH_QUEUE_SERIAL)
    • NSLock
    • NSCondition
    • pthread_mutex(recursive)
    • NSRecursiveLock
    • NSConditionLock
    • @synchronized
    iOS多读单写实现方案

    dispatch_barrier_async

    dispatch_barrier_async又叫栅栏函数,这个函数传入的并发队列必须是自己通过dispatch_queue_cretate创建的,如果传入的是一个串行或是一个全局的并发队列,那这个函数便等同于dispatch_async函数的效果,使用栅栏函数可以保证同一时间只有一个线程可进行写操作,读操作可以有多少个线程进行。

    //初始化队列
    dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
    //读
    dispatch_async(queue, ^{
        
    });
    //写
    dispatch_barrier_async(queue, ^{
        
    });
    

    相关文章

      网友评论

          本文标题:iOS线程同步(各种锁)

          本文链接:https://www.haomeiwen.com/subject/gwfgzltx.html