iOS 开发中加锁

[1].OSSpinLock 自旋锁

//导入头文件
#import

  /*
        //dispatch_get_global_queue 系统提供的一个并发队列
                        同步                  异步
        串行队列    当前线程,一个一个执行     其他线程,一个一个执行
        并行队列    当前线程,一个一个执行     开很多线程,一起执行
  */
//线程1
  
        __block OSSpinLock osLock = OS_SPINLOCK_INIT;dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程1 准备上锁");
    
        OSSpinLockLock(&oslock);
        sleep(4);
        NSLog(@"线程1");
        
       OSSpinLockUnlock(&oslock);
        NSLog(@"线程1 解锁成功");
        
        NSLog(@"-------------------------------");
    });

//线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程2 准备上锁");
        
        OSSpinLockLock(&oslock);
        NSLog(@"线程2");
        
        OSSpinLockUnlock(&oslock);
        NSLog(@"线程2 解锁成功");
        
        NSLog(@"################################");
    });

iOS 开发中加锁_第1张图片
屏幕快照 2017-01-11 下午1.56.41.png

[1]自旋锁与互斥锁有点类似,只是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。由于自旋锁使用者一般保持锁 时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。
[2]图中可以发现:当我们锁住线程1时,在同时锁住线程2的情况下,线程2会一直等待(自旋锁不会让等待的进入睡眠状态,直到线程1的任务执行完且解锁完毕,线程2会立即执行;正常情况下,lock 和unlock最好成对出现。
[3]但是因为自旋锁存在优先级反转问题,简单说如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。所以苹果弄了os_unfair_lock来代替自旋锁但是 只有IOS10 中才有~~~

[2]. dispatch_semaphore 信号量


//传入值必须 >=0, 若传入为0则阻塞线程并等待timeout,时间到后会执行其后的语句
    dispatch_semaphore_t signal = dispatch_semaphore_create(1);
    
    //设置timeOut NSEC_PER_SEC表示在当前时间延时3秒
    dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, 3.0f * NSEC_PER_SEC);
    
    /*
        
     //常用的三个函数
     dispatch_semaphore_create(1): 传入值必须 >=0, 若传入为 0 则阻塞线程并等待timeout,时间到后会执行其后的语句
     dispatch_semaphore_wait(signal, overTime):可以理解为 lock,会使得 signal 值 -1
     dispatch_semaphore_signal(signal):可以理解为 unlock,会使得 signal 值 +1
     */
    
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程1 等待ing");
        dispatch_semaphore_wait(signal, overTime); //signal 值 -1
        
        NSLog(@"线程1");
        dispatch_semaphore_signal(signal); //signal 值 +1
        
        NSLog(@"线程1 发送信号");
        NSLog(@"--------------------------------------------");
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程2 等待ing");
        dispatch_semaphore_wait(signal, overTime);
        
        NSLog(@"线程2");
        dispatch_semaphore_signal(signal);
        
        NSLog(@"线程2 发送信号");
    });
iOS 开发中加锁_第2张图片
屏幕快照 2017-01-11 下午2.37.22.png

关于信号量,我们可以用停车来比喻:
停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。信号量的值(signal)就相当于剩余车位的数目,dispatch_semaphore_wait
函数就相当于来了一辆车,dispatch_semaphore_signal
就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),调用一次 dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait 剩余车位就减少一个;当剩余车位为 0 时,再来车(即调用 dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,所以就一直等下去。

[3]. pthread_mutex 互斥锁

/*
        pthread_mutex_init()函数是以动态方式创建互斥锁的,参数attr指定了新建互斥锁的属性。
        如果参数attr为NULL,则使用默认的互斥锁属性,默认属性为快速互斥锁 。以下是互斥锁的属性
        
        * PTHREAD_MUTEX_TIMED_NP,这是缺省值,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁。这种锁策略保证了资源分配的公平性。
     
       * PTHREAD_MUTEX_RECURSIVE_NP,嵌套锁,允许同一个线程对同一个锁成功获得多次,并通过多次unlock解锁。如果是不同线程请求,则在加锁线程解锁时重新竞争。
     
       * PTHREAD_MUTEX_ERRORCHECK_NP,检错锁,如果同一个线程请求同一个锁,则返回EDEADLK,否则与PTHREAD_MUTEX_TIMED_NP类型动作相同。这样就保证当不允许多次加锁时不会出现最简单情况下的死锁。
     
       * PTHREAD_MUTEX_ADAPTIVE_NP,适应锁,动作最简单的锁类型,仅等待解锁后重新竞争。
          
          pthread_mutex_init(&lock,NULL);初始化锁
          pthread_mutex_lock(&lock);上锁
          //do your stuff
          pthread_mutex_unlock(&lock);解锁
          pthread_mutex_destroy(&lock);销毁锁
     */
    
    
    static pthread_mutex_t pLock;
    
    pthread_mutex_init(&pLock, NULL);
    
    //1.线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程1 准备上锁");
        pthread_mutex_lock(&pLock);
        sleep(3);
        NSLog(@"线程1");
        pthread_mutex_unlock(&pLock);
        NSLog(@"------------------");
    });
    
    //1.线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程2 准备上锁");
        pthread_mutex_lock(&pLock);
        NSLog(@"线程2");
        pthread_mutex_unlock(&pLock);
        NSLog(@"##################  $");
    });

iOS 开发中加锁_第3张图片
屏幕快照 2017-01-11 下午2.58.09.png

使用互斥锁时要注意死锁,死锁主要发生在有多个依赖锁存在时, 会在一个线程试图以与另一个线程相反顺序锁住互斥量时发生. 如何避免死锁是使用互斥量应该格外注意的东西。
   总体来讲, 有几个不成文的基本原则:
  对共享资源操作前一定要获得锁。
  完成操作以后一定要释放锁。
  尽量短时间地占用锁。
  如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC。
  线程错误返回时应该释放它所获得的锁。

[4].NSLock 普通锁

//NSLock 普通锁
    /*
        lock、unlock:不多做解释,和上面一样
        trylock:能加锁返回 YES 并执行加锁操作,相当于 lock,反之返回 NO
        lockBeforeDate:这个方法表示会在传入的时间内尝试加锁,若能加锁则执行加锁操作并返回 YES,反之返回 NO
     */
    
    
    NSLock *lock = [NSLock new];
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程1 尝试加锁ing...");
        [lock lock];
        sleep(5);//睡眠5秒
        NSLog(@"线程1");
        [lock unlock];
        NSLog(@"线程1解锁成功");
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程2 尝试加锁ing...");
        BOOL x =  [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:4]];
        if (x) {
            NSLog(@"线程2");
            [lock unlock];
        }else{
            NSLog(@"失败");
        }
    });
iOS 开发中加锁_第4张图片
屏幕快照 2017-01-11 下午3.38.09.png

[5]. NSCondition

//NSCondition
    /*
        wait:进入等待状态
        waitUntilDate::让一个线程等待一定的时间
        signal:唤醒一个等待的线程
        broadcast:唤醒所有等待的线程
     */
    
    
    NSCondition *cLock = [NSCondition new];
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [cLock lock];
        NSLog(@"线程1加锁成功");
        [cLock wait];
        NSLog(@"线程1");
        [cLock unlock];
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [cLock lock];
        NSLog(@"线程2加锁成功");
        [cLock wait];
        NSLog(@"线程2");
        [cLock unlock];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(2);
        NSLog(@"唤醒一个等待的线程");
        [cLock signal];
    });
iOS 开发中加锁_第5张图片
屏幕快照 2017-01-11 下午3.53.02.png

[6].@synchronized 条件锁

//线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @synchronized (self) {
            sleep(2);
            NSLog(@"线程1");
        }
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @synchronized (self) {
            NSLog(@"线程2");
        }
    });
iOS 开发中加锁_第6张图片
屏幕快照 2017-01-11 下午4.00.37.png

[7].NSConditionLock 条件锁

//NSConditionLock
    /*
        相比于 NSLock 多了个 condition 参数,我们可以理解为一个条件标示。

    
    */
    //线程1
    
    NSConditionLock *cLock = [[NSConditionLock alloc] initWithCondition:0];
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if([cLock tryLockWhenCondition:0]){
            NSLog(@"线程1");
            [cLock unlockWithCondition:1];
        }else{
            NSLog(@"失败");
        }
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [cLock lockWhenCondition:3];
        NSLog(@"线程2");
        [cLock unlockWithCondition:2];
    });
    
    //线程3
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [cLock lockWhenCondition:1];
        NSLog(@"线程3");
        [cLock unlockWithCondition:3];
    });
iOS 开发中加锁_第7张图片
屏幕快照 2017-01-11 下午4.04.48.png

我们在初始化 NSConditionLock 对象时,给了他的标示为 0
执行 tryLockWhenCondition:时,我们传入的条件标示也是 0,所 以线程1 加锁成功
执行 unlockWithCondition:时,这时候会把condition由 0 修改为 1
因为condition 修改为了 1, 会先走到 线程3,然后 线程3 又将 condition 修改为 3
最后 走了 线程2 的流程
从上面的结果我们可以发现,NSConditionLock 还可以实现任务之间的依赖。

你可能感兴趣的:(iOS 开发中加锁)