iOS多线程(四)

多线程安全隐患解决方案

1.解决方案:使用线程同步技术(协同步调,按预定的先后次序进行)

2.常用的线程同步技术:加锁

3.iOS中的线程同步方案

OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized

3.1OSSpinLock

3.1.1OSSpinLock叫做自旋锁,等待锁的线程会处于忙等(busy-wait)状态,一直占用CPU资源
3.1.2 目前已经不安全,可能会出现优先级反转问题
3.1.3 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法 释放锁
3.1.4 头文件 #import 
3.1.5 示例代码如下
- (void)p_saleTicket {
//初始化
    static OSSpinLock ossPinLock = OS_SPINLOCK_INIT;
// 尝试加锁(如果需要等待就不加锁,直接返回false,如果不需要等待就加锁返回true)
//bool result  = OSSPinLockTry(&ossPinLock);
//加锁
    OSSpinLockLock(&ossPinLock);
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
//解锁
    OSSpinLockUnlock(&ossPinLock);
}

3.2 os_unfair_lock

3.2.1 os_unfair_lock用于取代不安全的OSSPinLock,iOS10开始支持
3.2.2从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
3.2.3头文件#import
3.2.4示例代码如下
- (void)p_saleTicket3 {
    //初始化
    static os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
    //尝试加锁
    //os_unfair_lock_trylock(&lock);
    //加锁
    os_unfair_lock_lock(&lock);
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
    //解锁
    os_unfair_lock_unlock(&lock);
}

3.3 pthread_mutex_t

3.2.1 mutex叫做"互斥锁 ",等待锁的线程会处于休眠状态
3.2.2头文件#import 
3.2.3示例代码如下
// 静态初始化
  //        pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  
//    // 初始化属性
//    pthread_mutexattr_t attr;
//    pthread_mutexattr_init(&attr);
//    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
//    // 初始化锁
//    pthread_mutex_init(mutex, &attr);
//    // 销毁属性
//    pthread_mutexattr_destroy(&attr);
  
  // 初始化属性
//    pthread_mutexattr_t attr;
//    pthread_mutexattr_init(&attr);
//    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
  // 初始化锁
  pthread_mutex_init(mutex, NULL);
  // 销毁属性
//    pthread_mutexattr_destroy(&attr);

3.4 NSLock | NSRecursiveLock

  3.4.1.NSLock是对mutex普通锁的封装
  3.4.2.NSRecursiveLock也是对mutex递归锁的封装
 3.4.3示例代码如下
- (void)p_saleTicket {
   [self.lock lock];
   sleep(1);
   int oldCount = self.ticketsCount;
   oldCount--;
   self.ticketsCount = oldCount;
   NSLog(@"余票%d",oldCount);
   [self.lock unlock];
}

3.5 NSCondition

3.5.1NSCondition是对mutex和cond的封装
3.5.2常见API
- (void)wait; 让当前线程处于等待状态
- (BOOL)waitUntilDate:(NSDate *)limit;让当前线程在一定时间内处于等待状态
- (void)signal;CPU发信号告诉当前线程不用在等待,可以继续执行
- (void)broadcast;CPU发信号告诉所有线程不用在等待,可以继续执行
3.5.3示例代码如下
- (void)p_saleTicket {

    [self.lock lock];
    if (self.ticketsCount == 12) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            // 信号
            [self.lock signal];
        });
        // 等待
        [self.lock wait];
    }
    sleep(1);
    int oldCount = self.ticketsCount;
    oldCount--;
    self.ticketsCount = oldCount;
    NSLog(@"余票%d",oldCount);
    [self.lock unlock];

}

3.6 NSConditionLock

3.6.1示例代码如下
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}

- (void)__one
{
    [self.conditionLock lock];
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two
{
    [self.conditionLock lockWhenCondition:2];
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}

- (void)__three
{
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}

3.7 DISPATCH_QUEUE_SERIAL

3.7.4示例代码如下 放到串行队列里面

3.8 dispatch_semaphore_t

3.8.1semaphore叫做信号量
3.8.2信号量的初始值,可以用来控制线程并发访问的最大数量 不可以未负数
3.8.3信号量的初始值为1 ,代表同时只允许1条线程访问资源,保证线程同步
3.8.4示例代码如下
//    初始化信号量
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//    如果信号量=0当前线程就会进入休眠(直到信号量的值>0)
//    如果信号量>0 就减1 然后继续往下执行后面的代码
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//    让信号量的值加1
    dispatch_semaphore_signal(semaphore);

3.8 @synchronized

  3.8.1 @synchronized 是对mutex递归锁的封装
  3.8.2 @synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
  3.8.3 示例代码
@synchronized(obj){
}

练习代码git

https://gitee.com/tianmingfu/thread-synchronization.git

你可能感兴趣的:(iOS多线程(四))