• 锁iOS


    信号量:dispatch_semaphore_t

    互斥锁:pthread_mutex、@ synchronized、NSLock

    条件锁:NSConditionLock 、NSCondition、

    递归锁:NSRecursiveLock

    自旋锁:OSSpinLock(不安全,已遗弃,会出现优先级反转导致死锁的问题)

    读写锁:atomic(iOS10之后是os_unfair_lock来实现的)

    自旋锁和互斥锁的区别

    自旋锁会忙等: 所谓忙等,即在访问被锁资源时,调用者线程不会休眠,而是不停循环在那里,直到被锁资源释放锁。

    互斥锁会休眠: 所谓休眠,即在访问被锁资源时,调用者线程会休眠,此时cpu可以调度其他线程工作。直到被锁资源释放锁。此时会唤醒休眠线程。

    按性能由高到低:

    一、信号量dispatch_semaphore_t :性能高

    // 创建信号量,并给信号量初始化值
    dispatch_semaphore_t dispatch_semaphore_create(long value);
    // 信号量减1,当信号量<=0时阻塞线程
    long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
    // 信号量加1
    long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

    控制资源的同步访问

    // 创建信号量并初始化信号量的值为0
        dispatch_semaphore_t semaphone = dispatch_semaphore_create(0);
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{     // 线程1
            sleep(2);
            NSLog(@"async1.... %@",[NSThread currentThread]);
            dispatch_semaphore_signal(semaphone);//信号量+1
        });
        
        dispatch_semaphore_wait(semaphone, DISPATCH_TIME_FOREVER);//信号量减1
    
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 线程2
            sleep(2);
            NSLog(@"async2.... %@",[NSThread currentThread]);
            dispatch_semaphore_signal(semaphone);//信号量+1
        });

    最大并发数

       // 创建信号量并初始值为5,最大并发量5
       dispatch_semaphore_t semaphore =  dispatch_semaphore_create(5);
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        for (int i = 0;i < 100 ; i ++) {        
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);       
            dispatch_async(queue, ^{
                NSLog(@"i = %d  %@",i,[NSThread currentThread]);
                //此处模拟一个 异步下载图片的操作
                sleep(arc4random()%6);
                dispatch_semaphore_signal(semaphore);
            });
        }

    二、互斥锁pthread_mutex_t

    @property(nonatomic, assign)pthread_mutex_t _mutex;
    //初始化
    pthread_mutex_init(&_mutex, NULL);
    //加锁
    pthread_mutex_lock(&_mutex);
    //操作
    //解锁
    pthread_mutex_unlock(&_mutex);
    //销毁
    pthread_mutex_destroy(&_mutex);

    三、互斥锁NSLock(对pthread_mutex_t的封装)

    //初始化
    __block NSLock *lock = [[NSLock alloc] init];
    //加锁
    [lock lock];
    //解锁
    [lock unlock];

    四、条件锁NSCondition(对pthread_mutex_t的封装)

    - (void)conditionLock {
        NSCondition *conditionLock = [[NSCondition alloc] init];
        __block NSString *food;
        // 消费者1
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [conditionLock lock];
            if (!food) {// 没有现成的菜(判断是否满足线程阻塞条件)
                NSLog(@"等待上菜");
                [conditionLock wait];// 没有菜,等着吧!(满足条件,阻塞线程)
            }
            // 菜做好了,可以用餐!(线程畅通,继续执行)
            NSLog(@"开始用餐:%@",food);
            [conditionLock unlock];
        });
        // 消费者2
        //    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    //        [conditionLock lock];
    //        if (!food) {
    //            NSLog(@"等待上菜2");
    //            [conditionLock wait];
    //        }
    //        NSLog(@"开始用餐2:%@",food);
    //        [conditionLock unlock];
    //    });
    
        // 生产者
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [conditionLock lock];
            NSLog(@"厨师做菜中...");
            sleep(5);
            food = @"四菜一汤";
            NSLog(@"厨师做好了菜:%@",food);
            [conditionLock signal];
    //        [conditionLock broadcast];
            [conditionLock unlock];
        });
    }

    五、条件锁NSConditionLock(对NSCondition的封装)

    // 类似于信号量
        NSConditionLock * conditionLock = [[NSConditionLock alloc] initWithCondition:2];
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [conditionLock lockWhenCondition:1];
            NSLog(@"线程1");
            [conditionLock unlockWithCondition:0];
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
            [conditionLock lockWhenCondition:2];
            NSLog(@"线程2");
            [conditionLock unlockWithCondition:1];
        });
    
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            [conditionLock lock];
            NSLog(@"线程3");
            [conditionLock unlock];
        });

    打印结果:

    线程3 线程2 线程1   或者  线程2 线程1 线程3

    分析:

    -lockWhenCondition: 加锁,阻塞线程,如果condition与属性相等,被唤醒
    -unlockWithCondition: 解锁,并修改condition属性

    [conditionLock lock] 没有条件 多线程和 [conditionLock lockWhenCondition:2] 谁先执行,不确定

    六、递归锁NSRecursiveLock(对pthread_mutex_t的封装)

    七、互斥锁@synchronized : 性能低,更简洁易读易用

    @synchronized (self) {
    
    }

    另外

    栅栏函数控制多线程同步dispatch_barrier_sync 和 dispatch_barrier_async

    // 并发队列
        dispatch_queue_t queue = dispatch_queue_create("com.gcd.brrier", DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            sleep(1);
            NSLog(@"任务1 -- %@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            sleep(2);
            NSLog(@"任务2 -- %@",[NSThread currentThread]);
        });
        
        // 栅栏函数,修改同步栅栏和异步栅栏,观察“栅栏结束”的打印位置
        dispatch_barrier_sync(queue, ^{
            for (int i = 0; i < 4; i++) {
                NSLog(@"任务3 --- log:%d -- %@",i,[NSThread currentThread]);
            }
        });
        
        // 在这里执行一个输出
        NSLog(@"栅栏结束");
        
        dispatch_async(queue, ^{
            sleep(1);
            NSLog(@"任务4 -- %@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            sleep(2);
            NSLog(@"任务5 -- %@",[NSThread currentThread]);
        });

    原子操作atomic,原理是属性的set get方法加了锁,atomic是读/写操作安全的(操作的原子性),但不能保证多线程安全;nonatomic不能保证读写操作的安全(操作非原子性),也不能保证多线程安全;但是nonatomic的性能比atomic高,如果不涉及多线程操作,使用nonatomic是不错的选择,因为他可以保证性能的同时还能确保数据安全;如果开发中涉及到大量多线程操作,那么务必使用atomic,因为相对于性能而言,数据的正确安全更为重要。能在性能和安全之间找到平衡是最考验程序员的!!!

    // 添加一个atomic属性
    @property (atomic, copy) NSString *name;
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_async(concurrentQueue, ^{// 线程1
            self.name = @"张三";//setter操作
        });
        dispatch_async(concurrentQueue, ^{// 线程2
            self.name = @"李四";//setter操作
        });
        dispatch_async(concurrentQueue, ^{// 线程3
            self.name = @"王二";//setter操作
            // 这里有一个稍微耗时的操作,完事后想利用name的值,因为模拟需求是要把名字叫王二的人叫来
            sleep(2);
            NSLog(@"叫王二来一趟,name = %@",self.name);//getter操作
            
        });
        dispatch_async(concurrentQueue, ^{// 线程4
            self.name = @"麻子";//setter操作
        });

    打印结果:叫王二来一趟,name = 麻子

    扩展:

    一、三方库 SDWebImage 中的锁

    @property (nonatomic, strong) dispatch_semaphore_t lock;

    懒加载

    - (dispatch_semaphore_t)lock {
        if (!_lock) {
            _lock = dispatch_semaphore_create(1);
        }
        return _lock;
    }

    宏定义

    #ifndef SD_LOCK
    #define SD_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
    #endif
    
    #ifndef SD_UNLOCK
    #define SD_UNLOCK(lock) dispatch_semaphore_signal(lock);
    #endif

    加锁

    SD_LOCK(self.lock);

    解锁

    SD_UNLOCK(self.lock);
    在北京的灯中,有一盏是我家的。这个梦何时可以实现?哪怕微微亮。北京就像魔鬼训练营,有能力的留,没能力的走……
  • 相关阅读:
    20172318 2017-2018-2 《程序设计与数据结构》第5周学习总结
    20172318 2017-2018-2 《程序设计与数据结构》第4周学习总结
    20172318 2017-2018-2 《程序设计与数据结构》实验1报告
    20172318 《程序设计与数据结构》第三周学习总结
    20172318 2017-2018-2 《程序设计与数据结构》第2周学习总结
    20172318 2016-2017-2 《Java程序设计》第一周学习总结
    寒假作业03
    寒假作业02
    20172311 2017-2018-2 《程序设计与数据结构》第五周学习总结
    20172311 《程序设计与数据结构》第四周学习总结
  • 原文地址:https://www.cnblogs.com/huangzs/p/14506726.html
Copyright © 2020-2023  润新知