iOS中的锁

iOS中的锁

前言

写在前面:

  • 临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。

  • 自旋锁:是用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。 自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。

  • 互斥锁(Mutex):是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区而达成。

  • 读写锁:是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁) 用于解决多线程对公共资源读写问题。读操作可并发重入,写操作是互斥的。 读写锁通常用互斥锁、条件变量、信号量实现。

  • 信号量(semaphore):是一种更高级的同步机制,互斥锁可以说是semaphore在仅取值0/1时的特例。信号量可以有更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥。

  • 条件锁:就是条件变量,当进程的某些资源要求不满足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行。

  • 分布式锁: 分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁

对比自旋锁和互斥锁异同:

  • 相同点:都能保证同一时间只有一个线程访问共享资源。都能保证线程安全。

  • 不同点:

    • 互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁,则等待资源的线程会被唤醒。

    • 自旋锁:如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行。

    • 自旋锁的效率高于互斥锁。

iOS中常用的加锁机制:

  • @synchronized
  • dispatch_semaphore
  • NSLock
  • NSRecursiveLock
  • NSConditionLock
  • NSCondition
  • pthread_mutex
  • pthread_mutex(recursive)
  • OSSpinLock
  • osunfairlock
  • pthread_rwlockt
  • NSDistributedLock

代码

介绍

1. @synchronized

先上代码:

- (void)test {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @synchronized(self){
            NSLog(@"Thread one start ...");
            sleep(3);
            NSLog(@"Thread one end ...");
        }
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        @synchronized(self){
            NSLog(@"Thread two end ...");
        }
    });
}

执行结果:

2018-08-08 14:17:17.927986+0800 TreadTest[88534:2170294] Thread one start ...
2018-08-08 14:17:20.930640+0800 TreadTest[88534:2170294] Thread one end ...
2018-08-08 14:17:20.931061+0800 TreadTest[88534:2170296] Thread two end ...

@synchronized(obj)指令使用obj作为标识, 只有obj是同一个对象时, 餐能满足互斥条件;
特点:

  • synchronized会阻塞调用它的线程
  • @synchronized block 在被保护的代码上暗中添加了一个异常处理。同步某对象时如若抛出异常,锁会被释放掉。
  • @synchronized 后面需要紧跟一个 OC 对象,它实际上是把这个对象当做锁来使用。这是通过一个哈希表来实现的,OC 在底层使用了一个互斥锁的数组(你可以理解为锁池),通过对对象去哈希值来得到对应的互斥锁。

注意:

Objective-C类自身也是对象所以可以对这些类对象使用synchronized,此时是对整个对象类型进行线程同步。

@synchronized ([self class]) {
    }

关于 @synchronized,这儿比你想知道的还要多

2. dispatch_semaphore

代码:

- (void)test {
    dispatch_semaphore_t signal = dispatch_semaphore_create(1);
    dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
    int timeSleep = 2;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(signal, timeout);
        NSLog(@"SemaphoreTest Thread one start ...");
        sleep(timeSleep);
        NSLog(@"SemaphoreTest Thread one end ...");
        dispatch_semaphore_signal(signal);
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        dispatch_semaphore_wait(signal, timeout);
        NSLog(@"SemaphoreTest Thread two end ...");
        dispatch_semaphore_signal(signal);
    });
}

timeSleep=2, 输出:

2018-08-08 17:45:49.321698+0800 TreadTest[4121:2420094] SemaphoreTest Thread one start ...
2018-08-08 17:45:51.325003+0800 TreadTest[4121:2420094] SemaphoreTest Thread one end ...
2018-08-08 17:45:51.325509+0800 TreadTest[4121:2420096] SemaphoreTest Thread two end ...

timeSleep=4, 超时输出:

2018-08-08 17:46:45.776442+0800 TreadTest[4159:2421529] SemaphoreTest Thread one start ...
2018-08-08 17:46:48.779212+0800 TreadTest[4159:2421525] SemaphoreTest Thread two end ...
2018-08-08 17:46:49.777714+0800 TreadTest[4159:2421529] SemaphoreTest Thread one end ...

dispatch_semaphore是GCD用来同步的一种方式,相关函数:

  • dispatch_semaphore_create:

    dispatch_semaphore_t dispatch_semaphore_create(long value);

    传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量,参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。

  • dispatch_semaphore_signal

    long dispatch_semaphore_signal(dispatch_semaphore_t dsema)

    函数会使传入的信号量dsema的值加1;

  • dispatch_semaphore_wait

    long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

    这个函数会使传入的信号量dsema的值减1

    如果dsema信号量的值大于0,该函数所处线程就继续执行后边的语句,并且将信号量的值减1;

    如果desema的值为0,就阻塞当前线程等待timeout

    如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。

    如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句

    注意:

    dispatch_semaphore 是信号量,但当信号总量设为 1 时也可以当作锁来。

    在没有等待情况出现时,它的性能比 pthread_mutex 还要高,但一旦有等待情况出现时,性能就会下降许多。

    相对于 OSSpinLock 来说,它的优势在于等待时不会消耗 CPU 资源

    另外, 在设置timeout时, 有两个比较重要的宏, DISPATCH_TIME_NOWDISPATCH_TIME_FOREVER, 如果一直等待, 可以设置为dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

3. NSLock

NSLock是Cocoa最基本的锁对象, 主要包含四个方法:

    • (void)lock;
    • (void)unlock;
    • (BOOL)tryLock;
    • (BOOL)lockBeforeDate:(NSDate *)limit;

常规用法:

- (void)test {
    NSLock *lock = [NSLock new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"Thread one start ...");
        sleep(3);
        NSLog(@"Thread one end ...");
        [lock unlock];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"Thread two end ...");
        [lock unlock];
    });
}

输出:

2018-08-08 18:03:39.690424+0800 TreadTest[4511:2438866] Thread one start ...
2018-08-08 18:03:42.693269+0800 TreadTest[4511:2438866] Thread one end ...
2018-08-08 18:03:42.693767+0800 TreadTest[4511:2438865] Thread two end ...

尝试获取加锁:

- (void)testTry {
    NSLock *lock = [NSLock new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"Thread one start ...");
        sleep(3);
        NSLog(@"Thread one end ...");
        [lock unlock];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock tryLock]) {
            NSLog(@"Thread two get lock ...");
            [lock unlock];
        } else {
            NSLog(@"Thread two fail to get lock ...");
        }
    });
}

tryLock会尝试加锁, 如果加锁失败直接返回NO, 不会阻塞线程
输出:

2018-08-08 19:18:04.520605+0800 TreadTest[5962:2484928] Thread one start ...
2018-08-08 19:18:04.520609+0800 TreadTest[5962:2484929] Thread two fail to get lock ...
2018-08-08 19:18:07.523243+0800 TreadTest[5962:2484928] Thread one end ...

lockBeforeDate会在超时之前一直尝试获取加锁, 如果超则返回NO
获取超时:

- (void)testLockDate {
    NSLock *lock = [NSLock new];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"Thread one start ...");
        sleep(3);
        NSLog(@"Thread one end ...");
        [lock unlock];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:5]]) {
            NSLog(@"Thread two get lock ...");
            [lock unlock];
        } else {
            NSLog(@"Thread two fail to get lock ...");
        }
    });
}

输出:

2018-08-08 19:20:27.955699+0800 TreadTest[6043:2488268] Thread one start ...
2018-08-08 19:20:30.956558+0800 TreadTest[6043:2488268] Thread one end ...
2018-08-08 19:20:30.957228+0800 TreadTest[6043:2488272] Thread two get lock ...

4 NSRecursiveLock

- (void)test {
//    NSLock *lock = [[NSLock alloc] init];
    NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        static void (^RecursiveMethod)(int);
        
        RecursiveMethod = ^(int value) {
            [lock lock];
            if (value > 0) {
                NSLog(@"value = %d", value);
                sleep(1);
                RecursiveMethod(value - 1);
            }
            [lock unlock];
        };
        
        RecursiveMethod(5);
    });
}

输出:

2018-08-08 19:39:20.174989+0800 TreadTest[6500:2511354] value = 5
2018-08-08 19:39:21.180232+0800 TreadTest[6500:2511354] value = 4
2018-08-08 19:39:22.182961+0800 TreadTest[6500:2511354] value = 3
2018-08-08 19:39:23.186836+0800 TreadTest[6500:2511354] value = 2
2018-08-08 19:39:24.190228+0800 TreadTest[6500:2511354] value = 1

NSRecursiveLock实际上定义的是一个递归锁,这个锁可以被同一线程多次请求,而不会引起死锁。
递归锁会跟踪它被lock的次数。每次成功的lock都必须平衡调用unlock操作。只有所有达到这种平衡,锁最后才能被释放,以供其它线程使用.

如果将NSRecursiveLock改为NSLock, 则会引起死锁:

2018-08-08 19:36:25.251656+0800 TreadTest[6422:2507784] value = 5

另外, NSRecursiveLock提供了另外尝试加锁的方法:

    • (BOOL)tryLock;
    • (BOOL)lockBeforeDate:(NSDate *)limit;

5 NSConditionLock

 - (void)test {
    NSMutableArray *products = [NSMutableArray array];
//    NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:2];
    NSConditionLock *lock = [[NSConditionLock alloc] init];;

    NSInteger HAS_DATA = 1;
    NSInteger NO_DATA = 0;
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (1) {
            [lock lockWhenCondition:NO_DATA];
            [products addObject:[[NSObject alloc] init]];
            NSLog(@"produce a product,总量:%zi",products.count);
            [lock unlockWithCondition:HAS_DATA];
            sleep(1);
        }
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (1) {
            NSLog(@"wait for product");
            [lock lockWhenCondition:HAS_DATA];
            [products removeObjectAtIndex:0];
            NSLog(@"custome a product");
            [lock unlockWithCondition:NO_DATA];
        }
    });
}

输出:

2018-08-08 19:52:43.013061+0800 TreadTest[6848:2528186] wait for product
2018-08-08 19:52:43.013075+0800 TreadTest[6848:2528187] produce a product,总量:1
2018-08-08 19:52:43.013229+0800 TreadTest[6848:2528186] custome a product
2018-08-08 19:52:43.013396+0800 TreadTest[6848:2528186] wait for product
2018-08-08 19:52:44.014761+0800 TreadTest[6848:2528187] produce a product,总量:1
2018-08-08 19:52:44.014980+0800 TreadTest[6848:2528186] custome a product
2018-08-08 19:52:44.015118+0800 TreadTest[6848:2528186] wait for product
2018-08-08 19:52:45.018647+0800 TreadTest[6848:2528187] produce a product,总量:1
2018-08-08 19:52:45.018906+0800 TreadTest[6848:2528186] custome a product

NSConditionLock有一个很重要的属性:

@property (readonly) NSInteger condition;

执行[lock lockWhenCondition:value]时, 只有value== lock.condition时, 才能获得锁;

执行[lock unlockWithCondition:value]时, 就会把lock.condition设置为value, 以供其他线程按照该条件获取锁.

NSConditionLock可以接受不同的条件, 进而影响不同线程之间的加锁状态.

6 NSCondition

- (void)test {
    NSCondition *condition = [[NSCondition alloc] init];
    NSMutableArray *products = [NSMutableArray array];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (1) {
            [condition lock];
            NSLog(@"custome lock");
            if ([products count] == 0) {
                NSLog(@"wait for product");
                [condition wait];
            }
            [products removeObjectAtIndex:0];
            NSLog(@"custome a product");
            [condition unlock];
            NSLog(@"custome unlock");
        }
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (1) {
            [condition lock];
            NSLog(@"factory lock");
            [products addObject:[[NSObject alloc] init]];
            NSLog(@"produce a product,总量:%zi",products.count);
            [condition signal];
            NSLog(@"signal a product,总量:%zi",products.count);
            [condition unlock];
            NSLog(@"factory unlock");
            sleep(1);
        }
        
    });
}

输出:

2018-08-08 20:18:56.220689+0800 TreadTest[7570:2561183] custome lock
2018-08-08 20:18:56.220809+0800 TreadTest[7570:2561183] wait for product
2018-08-08 20:18:56.220908+0800 TreadTest[7570:2561187] factory lock
2018-08-08 20:18:56.221005+0800 TreadTest[7570:2561187] produce a product,总量:1
2018-08-08 20:18:56.221779+0800 TreadTest[7570:2561187] signal a product,总量:1
2018-08-08 20:18:56.221865+0800 TreadTest[7570:2561187] factory unlock
2018-08-08 20:18:56.221870+0800 TreadTest[7570:2561183] custome a product
2018-08-08 20:18:56.221985+0800 TreadTest[7570:2561183] custome unlock
2018-08-08 20:18:56.222044+0800 TreadTest[7570:2561183] custome lock
2018-08-08 20:18:56.222296+0800 TreadTest[7570:2561183] wait for product

NSCondition是一种最基本的条件锁。手动控制线程wait和signal。

[condition lock]一般用于多线程同时访问、修改同一个数据源,保证在同一时间内数据源只被访问、修改一次,其他线程的命令需要在lock 外等待,只到unlock ,才可访问;

[condition unlock]与lock 同时使用

[condition wait]让当前线程处于等待状态, 但是condition并未解锁

[condition signal]CPU发信号告诉线程不用在等待,可以继续执行, 但是需要当前线程unlock之后, 其他wait线程才能继续

7 pthread_mutex

递归锁

- (void)test {
    __block pthread_mutex_t theLock;
    pthread_mutex_init(&theLock, NULL);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        pthread_mutex_lock(&theLock);
        NSLog(@"需要线程同步的操作1 开始");
        sleep(3);
        NSLog(@"需要线程同步的操作1 结束");
        pthread_mutex_unlock(&theLock);
        
    });    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        pthread_mutex_lock(&theLock);
        NSLog(@"需要线程同步的操作2");
        pthread_mutex_unlock(&theLock);
        
    });
}

输出:

2018-08-08 20:29:14.289222+0800 TreadTest[7848:2573203] 需要线程同步的操作1 开始
2018-08-08 20:29:17.294626+0800 TreadTest[7848:2573203] 需要线程同步的操作1 结束
2018-08-08 20:29:17.295094+0800 TreadTest[7848:2573206] 需要线程同步的操作2

pthread_mutex需要引入#import <pthread/pthread.h>, 具体方法:

  • int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * _Nullable __restrict);

    创建一个锁

  • int pthread_mutex_lock(pthread_mutex_t *);

    加锁, 当锁已经在使用的时候,挂起等待

  • int pthread_mutex_trylock(pthread_mutex_t *);

    尝试加锁,当锁已经在使用的时候,返回为EBUSY,不会挂起等待

  • int pthread_mutex_unlock(pthread_mutex_t *);

    解锁

  • int pthread_mutexattr_destroy(pthread_mutexattr_t *)

    销毁锁, 使用完之后记得销毁.

8 pthread_mutex(recursive)

- (void)test {
    __block pthread_mutex_t theLock;
    //pthread_mutex_init(&theLock, NULL);
    
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&theLock, &attr);
    pthread_mutexattr_destroy(&attr);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        static void (^RecursiveMethod)(int);
        
        RecursiveMethod = ^(int value) {
            
            pthread_mutex_lock(&theLock);
            if (value > 0) {
                
                NSLog(@"value = %d", value);
                sleep(1);
                RecursiveMethod(value - 1);
            }
            pthread_mutex_unlock(&theLock);
        };
        
        RecursiveMethod(5);
    });
}

输出:

2018-08-08 21:31:16.271338+0800 TreadTest[12858:2652044] value = 5
2018-08-08 21:31:17.276607+0800 TreadTest[12858:2652044] value = 4
2018-08-08 21:31:18.281560+0800 TreadTest[12858:2652044] value = 3
2018-08-08 21:31:19.283039+0800 TreadTest[12858:2652044] value = 2
2018-08-08 21:31:20.284007+0800 TreadTest[12858:2652044] value = 1

pthread_mutex为了防止在递归的情况下出现死锁而出现的递归锁。

作用和NSRecursiveLock递归锁类似。

如果使用pthread_mutex_init(&theLock, NULL);初始化锁的话,上面的代码会出现死锁现象。

9 OSSpinLock

- (void)test {
    __block OSSpinLock theLock = OS_SPINLOCK_INIT;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        OSSpinLockLock(&theLock);
        NSLog(@"需要线程同步的操作1 开始");
        sleep(3);
        NSLog(@"需要线程同步的操作1 结束");
        OSSpinLockUnlock(&theLock);
        
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        OSSpinLockLock(&theLock);
        sleep(1);
        NSLog(@"需要线程同步的操作2");
        OSSpinLockUnlock(&theLock);
        
    });
    
}

输出:

2018-08-08 21:38:06.443968+0800 TreadTest[13109:2660908] 需要线程同步的操作1 开始
2018-08-08 21:38:09.448119+0800 TreadTest[13109:2660908] 需要线程同步的操作1 结束
2018-08-08 21:38:10.504616+0800 TreadTest[13109:2660909] 需要线程同步的操作2

需要导入#import <libkern/OSAtomic.h>
OSSpinLock 自旋锁,性能最高的锁;原理很简单,就是一直 do while 忙等。它的缺点是当等待时会消耗大量 CPU 资源,所以它不适用于较长时间的任务

自旋锁不会让等待的进入睡眠状态
不再安全的 OSSpinLock中说明了OSSpinLock已经不再安全,在iOS10.0中已经废弃。

10 os_unfair_lock

- (void)test {
    __block os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        os_unfair_lock_lock(&unfairLock);
        NSLog(@"需要线程同步的操作1 开始");
        sleep(3);
        NSLog(@"需要线程同步的操作1 结束");
        os_unfair_lock_unlock(&unfairLock);

    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if (os_unfair_lock_trylock(&unfairLock)) {
            sleep(1);
            NSLog(@"需要线程同步的操作2");
            os_unfair_lock_unlock(&unfairLock);
        } else {
            NSLog(@"lock fail");
        }

    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        os_unfair_lock_lock(&unfairLock);
        NSLog(@"需要线程同步的操作3 开始");
        sleep(3);
        NSLog(@"需要线程同步的操作3 结束");
        os_unfair_lock_unlock(&unfairLock);
        
    });
}

输出:

2018-08-09 11:36:37.785061+0800 TreadTest[23389:2849366] 需要线程同步的操作1 开始
2018-08-09 11:36:37.785102+0800 TreadTest[23389:2849365] lock fail
2018-08-09 11:36:40.790521+0800 TreadTest[23389:2849366] 需要线程同步的操作1 结束
2018-08-09 11:36:40.791035+0800 TreadTest[23389:2849364] 需要线程同步的操作3 开始
2018-08-09 11:36:43.795681+0800 TreadTest[23389:2849364] 需要线程同步的操作3 结束

输出结果可能不一定, 取决于那个线程先拿到锁

需要导入#import <os/lock.h>

自旋锁已经不在安全,然后苹果又整出来个 os_unfair_lock_t ,这个锁解决了优先级反转问题。

底层调用看,为互斥锁,等待os_unfair_lock锁的线程会处于休眠,而并非忙等,等不到锁就休眠(ios10以上使用)

os_unfair_lock_t unfairLock;     
unfairLock = &(OS_UNFAIR_LOCK_INIT);     
os_unfair_lock_lock(unfairLock);     
os_unfair_lock_unlock(unfairLock)

11 pthread_rwlock 读写锁

百度百科:读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者

测试读取操作:

- (void)testRead {
    __block pthread_rwlock_t rwlock;
    pthread_rwlock_init(&rwlock,NULL);
    
    int i = 4;
    while (i>0) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(1);
            
            NSLog(@"线程0:加锁");
            pthread_rwlock_rdlock(&rwlock);
            NSLog(@"线程0:读");
            pthread_rwlock_unlock(&rwlock);
            NSLog(@"线程0:解锁");
            
        });
        i--;
    }
}

输出:

2018-08-09 14:18:13.626237+0800 TreadTest[32346:2991544] 线程0:加锁
2018-08-09 14:18:13.626241+0800 TreadTest[32346:2991546] 线程0:加锁
2018-08-09 14:18:13.626320+0800 TreadTest[32346:2991545] 线程0:加锁
2018-08-09 14:18:13.626320+0800 TreadTest[32346:2991547] 线程0:加锁
2018-08-09 14:18:13.626779+0800 TreadTest[32346:2991544] 线程0:读
2018-08-09 14:18:13.626792+0800 TreadTest[32346:2991546] 线程0:读
2018-08-09 14:18:13.626973+0800 TreadTest[32346:2991545] 线程0:读
2018-08-09 14:18:13.627002+0800 TreadTest[32346:2991547] 线程0:读
2018-08-09 14:18:13.627102+0800 TreadTest[32346:2991544] 线程0:解锁
2018-08-09 14:18:13.627126+0800 TreadTest[32346:2991546] 线程0:解锁
2018-08-09 14:18:13.627221+0800 TreadTest[32346:2991545] 线程0:解锁
2018-08-09 14:18:13.627241+0800 TreadTest[32346:2991547] 线程0:解锁

不同线程之间并没有阻塞, 即没有等待其他线程解锁就可以进行读取

写操作:

- (void)testWrite {
    __block pthread_rwlock_t rwlock;
    pthread_rwlock_init(&rwlock,NULL);
    int j = 4;
    while (j>0) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(1);
            NSLog(@"线程1:加锁");
            pthread_rwlock_wrlock(&rwlock);
            NSLog(@"线程1:写");
            pthread_rwlock_unlock(&rwlock);
            NSLog(@"线程1:解锁");
        });
        j--;
    }
}

输出:

2018-08-09 14:24:48.828117+0800 TreadTest[32581:3000922] 线程1:加锁
2018-08-09 14:24:48.828117+0800 TreadTest[32581:3000937] 线程1:加锁
2018-08-09 14:24:48.828140+0800 TreadTest[32581:3000920] 线程1:加锁
2018-08-09 14:24:48.828178+0800 TreadTest[32581:3000921] 线程1:加锁
2018-08-09 14:24:48.828555+0800 TreadTest[32581:3000922] 线程1:写
2018-08-09 14:24:48.829907+0800 TreadTest[32581:3000922] 线程1:解锁
2018-08-09 14:24:48.829908+0800 TreadTest[32581:3000937] 线程1:写
2018-08-09 14:24:48.830625+0800 TreadTest[32581:3000937] 线程1:解锁
2018-08-09 14:24:48.830638+0800 TreadTest[32581:3000920] 线程1:写
2018-08-09 14:24:48.830981+0800 TreadTest[32581:3000920] 线程1:解锁
2018-08-09 14:24:48.830985+0800 TreadTest[32581:3000921] 线程1:写
2018-08-09 14:24:48.831438+0800 TreadTest[32581:3000921] 线程1:解锁

输出结果显示, 线程的写操作需要等待解锁(sleep时间不同可能造成输出顺序不同, 应该是和线程的时间片有关)

12 NSDistributedLock 分布式锁

分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁

  • 可以用来限制对某些共享资源的访问,比如一个文件。
  • 没有实现NSLocking协议,所有没有lock方法。一个lock方法将会阻塞线程的执行,并要求系统以预定的速度轮询锁。
  • 它提供了一个tryLock方法,并让你决定是否轮询。访问结束后,可以通过调用unlock方法来释放它
    处理多个进程或多个程序之间互斥问题
  • 一个获取锁的进程或程序在释放锁之前挂掉,锁不会被释放,可以通过breakLock方法解锁

Mac下NSDistributedLock同步性能

参考:

iOS开发中的11种锁以及性能对比

iOS中保证线程安全的几种方式与性能对比

OC--各种线程锁

iOS底层原理-多线程

Lock in iOS

iOS 锁的简单实现与总结

iOS-线程安全

互斥锁的实现

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • 锁是一种同步机制,用于多线程环境中对资源访问的限制iOS中常见锁的性能对比图(摘自:ibireme): iOS锁的...
    LiLS阅读 1,507评论 0 6
  • 在平时的开发中经常使用到多线程,在使用多线程的过程中,难免会遇到资源竞争的问题,那我们怎么来避免出现这种问题那? ...
    IAMCJ阅读 3,083评论 2 25
  • 本文不介绍各种锁的高级用法,只是整理锁相关的知识点,帮助理解。 锁的作用 防止在多线程(多任务)的情况下对共享资源...
    HelloiWorld阅读 2,883评论 0 8
  • 本文节选自成长手册 文章推荐和参考深入理解 iOS 开发中的锁pthread的各种同步机制 多线程编程被普遍认为复...
    百草纪阅读 2,793评论 1 9
  • 习惯听雨, 听一种孤独, 滑破夜空, 穿透寂静的声音… 习惯有你, 茫茫尘世, 两颗心靠近的距离, 汇成一点… 习...
    MFC梅阅读 2,051评论 52 81