2.iOS你了解的锁有哪些?

什么是锁?

在计算机科学中,锁是一种同步机制.用于在存在多线程的环境中实施对资源访问的限制.可以理解它的作用是排除并发的一种策略!

在iOS中,锁大致可分为递归锁,条件锁,分布式锁,一般锁(根据NSLock类中的分类进行划分).

常用的有一下几种:

  • @synchronized 互斥锁
  • NSLock 互斥锁
  • NSCondition 条件锁
  • NSConditionLock 条件锁
  • NSRecursiveLock 递归锁
  • pthread_mutex 互斥锁(C语言)
  • dispatch_semaphore 信号量实现加锁(GCD)
  • OSSpinLock 自旋锁
lock.png

自旋锁和互斥锁对比?

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

不同点:
互斥锁:如果共享资源已经有其他线程加锁了,线程会进入休眠状态等待加锁.一旦被访问的资源被解锁了,则等待资源的线程会立即执行.

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

自旋锁的效率要高于互斥锁

使用自旋锁时要注意什么?
由于自旋时不释放CPU,因此持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在那里自旋,这样会浪费CPU时间.

持有自旋锁的线程在sleep之前应该释放自旋锁以便于其他可以获取该自旋锁.内核编程中,如果持有自旋锁的代码sleep了就有可能导致整个系统挂起.

使用任何锁都是需要消耗系统资源(内存资源和CPU时间),这种资源消耗可以分为两类:

  • 建立锁所需要的资源
  • 当线程被阻塞时所需要的资源

两种锁的加锁原理:
互斥锁: 线程会从sleep(加锁)->running(解锁),过程中有上下文的切换,CPU的抢占,信号的发送等开销.
自旋锁: 线程一直在running(加锁->解锁),死循环检测锁的标志位,机制不复杂.

在iOS中实现自旋锁(以OSSpinLock为例)

导入头文件 #import

代码实现:
    __block OSSpinLock oslock = OS_SPINLOCK_INIT;
    //创建线程1 执行加锁操作,并sleep4秒之后执行解锁操作
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程1 准备加锁");
        OSSpinLockLock(&oslock);
        NSLog(@"线程1 加锁成功");
        sleep(4);
        OSSpinLockUnlock(&oslock);
        NSLog(@"线程1 解锁成功");
    });
    //创建线程2 执行加锁和解锁操作
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程2 准备加锁");
        OSSpinLockLock(&oslock);
        NSLog(@"线程2 加锁成功");
        OSSpinLockUnlock(&oslock);
        NSLog(@"线程2 解锁成功");
        NSLog(@"---------------------------");
    });

可能出现的结果如下:

第一种情况:

2021-01-05 14:19:31.877927+0800 test1[24762:377439] 线程2 准备加锁
2021-01-05 14:19:31.877936+0800 test1[24762:377442] 线程1 准备加锁
2021-01-05 14:19:31.878130+0800 test1[24762:377439] 线程2 加锁成功
2021-01-05 14:19:31.878258+0800 test1[24762:377439] 线程2 解锁成功
2021-01-05 14:19:31.878379+0800 test1[24762:377439] ---------------------------
2021-01-05 14:19:31.879170+0800 test1[24762:377442] 线程1 加锁成功
2021-01-05 14:19:35.884416+0800 test1[24762:377442] 线程1 解锁成功

第二种情况:

2021-01-05 14:23:03.797474+0800 test1[25001:381819] 线程1 准备加锁
2021-01-05 14:23:03.797780+0800 test1[25001:381819] 线程1 加锁成功
2021-01-05 14:23:03.799260+0800 test1[25001:381817] 线程2 准备加锁
2021-01-05 14:23:07.802987+0800 test1[25001:381819] 线程1 解锁成功
2021-01-05 14:23:07.849122+0800 test1[25001:381817] 线程2 加锁成功
2021-01-05 14:23:07.849530+0800 test1[25001:381817] 线程2 解锁成功
2021-01-05 14:23:07.849778+0800 test1[25001:381817] ---------------------------
互斥锁的实现(以pthread_mutex为例)

导入头文件#import

代码实现:
    static pthread_mutex_t plock;
    pthread_mutex_init(&plock, NULL);
    
    //创建线程1 执行加锁操作,并sleep4秒之后执行解锁操作
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"线程1 准备加锁");
        pthread_mutex_lock(&plock);
        NSLog(@"线程1 加锁成功");
        sleep(4);
        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(@"线程2 解锁成功");
        NSLog(@"------------------");
    }); 

可能出现的结果如下:

第一种情况:

2021-01-05 14:44:15.233326+0800 test1[26503:409089] 线程2 准备加锁
2021-01-05 14:44:15.233326+0800 test1[26503:409092] 线程1 准备加锁
2021-01-05 14:44:15.233550+0800 test1[26503:409092] 线程1 加锁成功
2021-01-05 14:44:19.239138+0800 test1[26503:409092] 线程1 解锁成功
2021-01-05 14:44:19.239143+0800 test1[26503:409089] 线程2 加锁成功
2021-01-05 14:44:19.239331+0800 test1[26503:409089] 线程2 解锁成功
2021-01-05 14:44:19.239463+0800 test1[26503:409089] ------------------

第二种情况:

2021-01-05 14:46:16.258085+0800 test1[26669:412086] 线程2 准备加锁
2021-01-05 14:46:16.258094+0800 test1[26669:412088] 线程1 准备加锁
2021-01-05 14:46:16.258308+0800 test1[26669:412086] 线程2 加锁成功
2021-01-05 14:46:16.258475+0800 test1[26669:412086] 线程2 解锁成功
2021-01-05 14:46:16.258480+0800 test1[26669:412088] 线程1 加锁成功
2021-01-05 14:46:16.258611+0800 test1[26669:412086] ------------------
2021-01-05 14:46:20.262960+0800 test1[26669:412088] 线程1 解锁成功

你可能感兴趣的:(2.iOS你了解的锁有哪些?)