锁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);

 

posted @ 2021-03-09 17:15  黄增松  阅读(123)  评论(0编辑  收藏  举报