锁的笔记

线程锁的几种介绍

  1. 互斥锁(又名同步锁)。即只有当锁处于打开状态下才能被使用。NSLock,或者@synchronized,C里用pthread_mutex_t。说的都是一个东西,这个没什么好说的,就是简单,好用,线程安全的好帮手,主要是防止几个线程同时操作一个可变容器。
  2. 递归锁。NSRecursiveLock主要是用在循环递归的时候来加锁的。这个东西是允许重复加锁,只要加锁和解锁的次数一致就会释放这个锁(同一线程内部受这个限制,不同线程才需要他收支平衡才能开锁),否则即死锁。
  3. 条件锁。NSConditionLock当满足条件的时候这个锁才会解开。当我们在使用多线程的时候,有时一把只会lock和unlock的锁未必就能完全满足我们的使用。因为普通的锁只能关心锁与不锁,而不在乎用什么钥匙才能开锁,而我们在处理资源共享的时候,多数情况是只有满足一定条件的情况下才能打开这把锁。

下面举例说明下

NSLock *theLock = [[NSLock alloc] init];
    
// 创建递归方法
static void (^testCode)(int);
testCode = ^(int value) {
    [theLock lock];
    if (value > 0)
    {
        NSLog(@"int:%d",value);
        [NSThread sleepForTimeInterval:1];
        testCode(value - 1);
    }
    NSLog(@"next");
    [theLock unlock];
};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    testCode(5);
});
    

这个栗子有兴趣的朋友可以run下,根本走不到打印next的那,为什么呢??

原因很简单,因为当进入到递归里的时候,刚进入testCode(value - 1);的时候就走不动了,因为这个锁被互斥了,被锁住了。所以下面的代码都不能往下走了。这就是互斥的好处,安全。但不好的是,这里并没有达到我们的预期,没有轮询往下走。

现在我们改下。把NSLock改成我们的递归锁

NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];

然后我们继续走,这次就不会被锁死了,会一直到出现1为止。

这就是递归锁的神奇之处,因为我们是在一个线程里,所以不会因为我一直没解锁,还终止递归。


NSConditionLock 相关知识点:

  1. NSConditionLock 是锁,一旦一个线程获得锁,其他线程一定等待
  2. [xxxx lock]; 表示 xxx 期待获得锁,如果没有其他线程获得锁(不需要判断内部的condition) 那它能执行此行以下代码,如果已经有其他线程获得锁(可能是条件锁,或者无条件锁),则等待,直至其他线程解锁
  1. [xxx lockWhenCondition:A条件]; 表示如果没有其他线程获得该锁,但是该锁内部的condition不等于A条件,它依然不能获得锁,仍然等待。如果内部的condition等于A条件,并且没有其他线程获得该锁,则进入代码区,同时设置它获得该锁,其他任何线程都将等待它代码的完成,直至它解锁。
  1. [xxx unlockWithCondition:A条件]; 表示释放锁,同时把内部的condition设置为A条件

下面看个栗子


NSConditionLock *lock = [[NSConditionLock alloc] init];
NSMutableArray *products=[NSMutableArray array];
NSInteger HAS_DATA=1;
NSInteger NO_DATA=0;
// 线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
        while(1){
            [lock lockWhenCondition:NO_DATA];
            [products addObject:[[NSObject alloc] init]];
            NSLog(@"produceaproduct,总量:%zi",products.count);
            [lock unlockWithCondition:HAS_DATA];
            sleep(1);
        }
    });
 // 线程2
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),^{
        while(1){
            [lock lockWhenCondition:HAS_DATA];
            NSLog(@"waitforproduct");
            [products removeObjectAtIndex:0];
            NSLog(@"customeaproduct");
            [lock unlockWithCondition:NO_DATA];
        }
    });

解释下:

因为我们默认的没有给condition,所以是0,故能进入线程1,进入线程1后,到最后会用1的条件来让别人解锁。同时让自身的condition重置成1,这样做为了让线程2不在等待。这样就能顺利的让线程2接力,线程2在结束的时候又把球抛给线程1,这样1来2去,就不会死锁。

以上便是常用的锁的常用解释。这些都是线程锁。
下面还有一个进程锁,可以用来堵塞进程的,信号量

信号量的创建:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
这个参数必须是>=0不然这个semaphore就会返回null。
dispatch_semaphore_wait信号量-1,dispatch_semaphore_signal信号量+1.

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
// 线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"yyy");
        [NSThread sleepForTimeInterval:5];
        dispatch_semaphore_signal(semaphore);
    });

// 线程2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"xxxx");
        dispatch_semaphore_signal(semaphore);
    });

如果是semaphore创建的时候信号量是0的时候,谁也别想走了,全部挂起。

线程1里,进去就休眠5秒,在这期间,谁也不能run,全部挂起,直到5秒后,信号量+1后才都恢复生机。线程2也一样的逻辑。

你可能感兴趣的:(锁的笔记)