oc的锁

  • 先说个概念:什么是线程安全
    Thread-safe code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfil their design specifications without unintended interaction.
    简单翻译一下就是:
    多线程操作共享数据没有出现意外的结果就是线程安全的,否则,是线程不安全的。
    举个:
    周杰伦演唱会还剩下20张票,开了2个窗口在卖,用一段代码来演示这个结果:
- (void)sellTicket {
    self.ticketCount = 20;
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        for (int i = 0; i < 20; i++) {
            if (self.ticketCount > 0) {
                self.ticketCount--;
                 NSLog(@"还剩%ld张票", self.ticketCount);
            }  
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 20; i++) {
            if (self.ticketCount > 0) {
                self.ticketCount--;
                 NSLog(@"还剩%ld张票", self.ticketCount);
            }  
        }
    });
}
// 运行结果
2019-06-18 11:48:51.510018+0800 SanJIDemo[46667:17651652] 还剩18张票
2019-06-18 11:48:51.510018+0800 SanJIDemo[46667:17651651] 还剩19张票
2019-06-18 11:48:51.510185+0800 SanJIDemo[46667:17651651] 还剩17张票
2019-06-18 11:48:51.510186+0800 SanJIDemo[46667:17651652] 还剩16张票
2019-06-18 11:48:51.510285+0800 SanJIDemo[46667:17651652] 还剩15张票
2019-06-18 11:48:51.510287+0800 SanJIDemo[46667:17651651] 还剩15张票
2019-06-18 11:48:51.510354+0800 SanJIDemo[46667:17651652] 还剩14张票
2019-06-18 11:48:51.510365+0800 SanJIDemo[46667:17651651] 还剩13张票
2019-06-18 11:48:51.510445+0800 SanJIDemo[46667:17651652] 还剩12张票
2019-06-18 11:48:51.511265+0800 SanJIDemo[46667:17651651] 还剩11张票
2019-06-18 11:48:51.511271+0800 SanJIDemo[46667:17651652] 还剩10张票
2019-06-18 11:48:51.511664+0800 SanJIDemo[46667:17651651] 还剩9张票
2019-06-18 11:48:51.511689+0800 SanJIDemo[46667:17651652] 还剩8张票
2019-06-18 11:48:51.512257+0800 SanJIDemo[46667:17651651] 还剩7张票
2019-06-18 11:48:51.512416+0800 SanJIDemo[46667:17651652] 还剩6张票
2019-06-18 11:48:51.512529+0800 SanJIDemo[46667:17651651] 还剩5张票
2019-06-18 11:48:51.513581+0800 SanJIDemo[46667:17651651] 还剩4张票
2019-06-18 11:48:51.513618+0800 SanJIDemo[46667:17651652] 还剩3张票
2019-06-18 11:48:51.513712+0800 SanJIDemo[46667:17651651] 还剩2张票
2019-06-18 11:48:51.513770+0800 SanJIDemo[46667:17651652] 还剩1张票
2019-06-18 11:48:51.513853+0800 SanJIDemo[46667:17651651] 还剩0张票

票并没有一张的确出现了奇怪的状况,这就是线程不安全导致的结果。
如何避免线程不安全呢?那就单线程工作呗,绝对万无一失,那开一个窗口卖票?效率未免太低了吧,所以我们需要线程锁来保证线程安全。

  • @synchronized
    dispatch_async(queue, ^{
        for (int i = 0; i < 20; i++) {
                @synchronized(self) {
                 if (self.ticketCount > 0) {
                    self.ticketCount--;
                    NSLog(@"还剩%ld张票", self.ticketCount);
                }
            }
        }
    });
// 结果
2019-06-18 14:06:01.431450+0800 SanJIDemo[48009:17697855] 还剩19张票
2019-06-18 14:06:01.431664+0800 SanJIDemo[48009:17697855] 还剩18张票
2019-06-18 14:06:01.431749+0800 SanJIDemo[48009:17697855] 还剩17张票
2019-06-18 14:06:01.431868+0800 SanJIDemo[48009:17697855] 还剩16张票
2019-06-18 14:06:01.432415+0800 SanJIDemo[48009:17697855] 还剩15张票
2019-06-18 14:06:01.432505+0800 SanJIDemo[48009:17697855] 还剩14张票
2019-06-18 14:06:01.432608+0800 SanJIDemo[48009:17697854] 还剩13张票
2019-06-18 14:06:01.432692+0800 SanJIDemo[48009:17697854] 还剩12张票
2019-06-18 14:06:01.432899+0800 SanJIDemo[48009:17697854] 还剩11张票
2019-06-18 14:06:01.433098+0800 SanJIDemo[48009:17697854] 还剩10张票
2019-06-18 14:06:01.433366+0800 SanJIDemo[48009:17697854] 还剩9张票
2019-06-18 14:06:01.433587+0800 SanJIDemo[48009:17697854] 还剩8张票
2019-06-18 14:06:01.433812+0800 SanJIDemo[48009:17697854] 还剩7张票
2019-06-18 14:06:01.434039+0800 SanJIDemo[48009:17697855] 还剩6张票
2019-06-18 14:06:01.434279+0800 SanJIDemo[48009:17697854] 还剩5张票
2019-06-18 14:06:01.434692+0800 SanJIDemo[48009:17697854] 还剩4张票
2019-06-18 14:06:01.434777+0800 SanJIDemo[48009:17697854] 还剩3张票
2019-06-18 14:06:01.435015+0800 SanJIDemo[48009:17697854] 还剩2张票
2019-06-18 14:06:01.436214+0800 SanJIDemo[48009:17697854] 还剩1张票
2019-06-18 14:06:01.436300+0800 SanJIDemo[48009:17697854] 还剩0张票

结果显示正常,票一张一张的卖完了,这就是线程安全的结果。


  • NSLock 内部封装了一个 pthread_mutex,属性为 PTHREAD_MUTEX_ERRORCHECK
    dispatch_async(queue, ^{
        for (int i = 0; i < 20; i++) {
             [self.lock lock];
             if (self.ticketCount > 0) {
                self.ticketCount--;
                NSLog(@"还剩%ld张票", self.ticketCount);
            }
             [self.lock unlock];
        }
    });
// 运行结果同上

  • dispatch_barrier_async设置同步锁,把self.ticketCount的读写操作放在一个并发队列里,同时setter设置栅栏函数异步,getter同步读取,保证self.ticketCount的线程安全。
- (void)setTicketCount:(NSInteger)ticketCount {
    dispatch_barrier_async(self.queue, ^{
        _ticketCount = ticketCount;
    });
}

- (NSInteger)ticketCount {
    __block NSInteger temp;
    dispatch_sync(self.queue, ^{
        temp = _ticketCount;
    });
    
    return temp;
}

- (dispatch_queue_t)queue {
    if (!_queue) {
        _queue =  dispatch_queue_create("wwww", DISPATCH_QUEUE_CONCURRENT);
    }
    
    return _queue;
}

这里有个注意的点:barrier只支持dispatch_queue_create创建出来的并发队列,queue.h文档里说了:

When submitted to a a global queue or to a queue not created with the
 * DISPATCH_QUEUE_CONCURRENT attribute, barrier blocks behave identically to
 * blocks submitted with the dispatch_async()/dispatch_sync() API.

翻译下:

当栅栏函数往一个全局并发队列或者不是由`DISPATCH_QUEUE_CONCURRENT `创建出来的,
栅栏函数表现得会跟`dispatch_async()/dispatch_sync()`一样,起不到栅栏的作用。

  • dispatch_semaphore
    有三个方法:
    dispatch_semaphore_create创建信号
    dispatch_semaphore_wait信号等待
    dispatch_semaphore_signal放开信号
    字面理解就是信号量的意思,可以理解为高速路口的栏杆,如果dispatch_semaphore_create(N),如果 N = 1,每个信号只能放行一个车,等待下一个信号放出才能放行下一辆车,所以创建信号量为1的信号可以做到线程安全。
- (void)sellTicket {
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    self.ticketCount = 20;
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0 );
    dispatch_async(queue, ^{
        for (int i = 0; i < 20; i++) {
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            if (self.ticketCount > 0) {
                self.ticketCount--;
                NSLog(@"还剩%ld张票", self.ticketCount);
           }
            
           dispatch_semaphore_signal(semaphore);
        }
    });
  
    dispatch_async(queue, ^{
        for (int i = 0; i < 20; i++) {
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
             if (self.ticketCount > 0) {
                self.ticketCount--;
                NSLog(@"还剩%ld张票", self.ticketCount);
            }
            
            dispatch_semaphore_signal(semaphore);
        }
    });
}

  • NSCondition条件锁
// 让当前线程处于等待状态
- (void)wait; 
//  让当前线程等待到某个日期
- (BOOL)waitUntilDate:(NSDate *)limit;  
// 释放信号
- (void)signal;
// 广播 ??
- (void)broadcast;

同时,NSCondition还遵循了一个protocol,这就是加锁与解锁的方法了,使用方法跟NSLock一样。

@protocol NSLocking

- (void)lock;
- (void)unlock;

@end

  • NSConditionLockNSCondition的进一步封装
    Apple官方文档的注释:
A lock that can be associated with specific, user-defined conditions.

  • OSSpinLock自旋锁,不再线程安全,具体参考不再安全的 OSSpinLock
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//加锁
OSSpinLockLock(&lock);
//解锁
OSSpinLockUnlock(&lock);

你可能感兴趣的:(oc的锁)