美文网首页
iOS 锁的介绍与实践

iOS 锁的介绍与实践

作者: 绍清_shao | 来源:发表于2020-03-31 15:40 被阅读0次

    前言

    锁是多个线程之间交互时需要同步的工具

    锁的类别

    1. Mutex (互斥)
    互斥充当资源周围的保护性屏障,有线程级互斥,也有进程级互斥。线程级互斥就是说,受保护的资源一次只能授予一个线程的访问权限。类似的,进程级互斥则表示,受保护的资源一次只能授予一个进程的访问权限。获得权限后,进程内所有线程都可以访问资源

    互斥锁是一种信号量,它一次只能授予对一个线程的访问权限。如果线程A正在使用互斥锁,而线程B试图获取该互斥锁,则线程B将被阻塞,直到该互斥锁被线程A释放为止。如果多个线程竞争同一个互斥锁,则一次只能访问一个。

    2. Recursive lock (递归锁)
    递归锁是Mutex(互斥锁)的一种变体。递归锁允许单个线程在释放它之前多次获取该锁。其他线程将保持阻塞状态,直到锁的所有者以获取锁的相同次数释放锁。

    举个例子:
    线程A获得递归锁,线程A没释放递归锁前又获得一次递归锁,此时线程A虽然没有释放递归锁,但任然能获得递归锁。此时线程B想获得递归锁,则线程B将被阻塞,因为线程A没以加锁的次数释放递归锁。等到线程A以加锁的次数释放递归锁,此时线程B就能获得递归锁了。

    3. Read-write lock(读写锁)
    读写锁也称为共享独占锁,多个读操作可以并发,写操作将串行执行。

    类似GCD的栅栏函数,例如有a-z个任务,其中e、m这两个操作是写操作。执行期间,当要开始e这个写操作时,会先把所以将要执行读跟写的操作都阻塞,然后开始执行e,等e执行完后,其他读操作将继续并发执行。下次遇到写操作时,依然如此。

    4. Distributed lock(分布式锁)
    分布式锁在进程级别提供互斥访问。与真正的互斥锁不同,分布式锁不会阻止进程或阻止其运行。它仅报告锁繁忙的时间,并让进程决定如何进行。

    ** 5. 自旋锁
    互斥锁是在CPU sleep-wait模式下阻塞线程,如果一个线程获取锁时被阻塞,CPU会把这个线程挂起,CPU会切换到其他线程进行任务执行。直到要获得的锁解开,会重新唤起被阻塞的线程进行任务执行。

    自旋锁是忙等模式,当一个线程获取自旋锁被阻塞时,CPU不会把这个线程挂起,也不会切换到其他线程执行代码。而且不断的询问锁是否已经解开,会处于忙等待。

    应用场景:临界资源执行速度非常短的时候,也就是被锁资源时间非常短暂的时候,CPU忙等的时间短,造成的CPU资源浪费小,且效率高

    缺点:优先级反转,当低优先级的线程获得锁后,高优先级线程想获取自旋锁,造成高优先级线程占用CPU资源,从而不能完成低优先级线程的任务,导致无法开锁。

    iOS中实践

    互斥锁

    1. mutex lock

    pthread_mutex_t mutex; // 互斥锁声明
    void MyInitFunction()
    {
        // 互斥锁初始化
        pthread_mutex_init(&mutex, NULL);
    }
     
    void MyLockingFunction()
    {
        // 互斥锁加锁
        pthread_mutex_lock(&mutex);
        // Do work.
        // 互斥锁解锁
        pthread_mutex_unlock(&mutex);
    }
    
    deinit() {
    // 释放互斥锁
    pthread_mutex_destroy
    }
    

    2. NSLock

    可以像使用任何互斥锁一样使用这些方法来获取和释放锁。

    除了标准的锁定行为,NSLock该类还添加了tryLocklockBeforeDate:方法。

    tryLock方法尝试获取锁,但如果锁不可用,则不会阻塞;相反,该方法仅返回NO。

    lockBeforeDate:方法尝试获取锁,在这期间会阻塞线程。但如果在指定的时间限制内未获取锁,则取消阻止线程(并返回NO)。

    总结:虽然都是互斥锁,比mutex lock要灵活很多,API也很人性化。

    BOOL moreToDo = YES;
    NSLock *theLock = [[NSLock alloc] init];
    while (moreToDo) {
        /* Do another increment of calculation */
        /* until there’s no more to do. */
        if ([theLock tryLock]) {
        /* Update display used by all threads. */
            [theLock unlock];
        }
    }
    

    3. synchronized指令
    代码中动态创建互斥锁的一种便捷方法,跟前面两种互斥锁达到的效果相同。synchronized指令不必直接创建互斥量或锁定对象。相反,只需像如下这样使用即可:

    - (void)myMethod:(id)anObj
    {
        @synchronized(anObj)
        {
            // Everything between the braces is protected by the @synchronized directive.
        }
    }
    

    如果在两个不同的线程中执行上述方法,并anObj在每个线程上为参数传递一个不同的对象,则每个线程将获得其锁并继续进行处理而不会被另一个线程阻塞。但是,如果在两种情况下都传递相同的对象,则其中一个线程将首先获取锁,而另一个线程将阻塞,直到第一个线程完成关键部分。

    递归锁 (可重入锁)

    NSRecursiveLock
    同一线程可以多次获取该锁,而不会导致线程死锁。

    每次成功获取锁都必须通过相应的调用来平衡,以解锁该锁。仅当所有锁定和解锁调用均达到平衡时,才实际释放该锁定,以便其他线程可以获取它。

    NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
     
    void MyRecursiveFunction(int value)
    {
        [theLock lock];
        if (value != 0)
        {
            --value;
            MyRecursiveFunction(value);
        }
        [theLock unlock];
    }
     
    MyRecursiveFunction(5);
    
    其他锁

    1. NSConditionLock略...
    2. NSDistributedLock略...

    参考链接

    • Apple官方文档
    • 《程序员的自我修养——链接、装载与库》第一章 第六节

    PS: 这是我见过把多线程、线程安全说的最明白清楚的文章了。强烈建议阅读这一章节

    相关文章

      网友评论

          本文标题:iOS 锁的介绍与实践

          本文链接:https://www.haomeiwen.com/subject/zjywuhtx.html