zoukankan      html  css  js  c++  java
  • iOS多线程编程之锁的理解

    一、需要互斥的例子

    在多线程环境中,无论哪个函数方法都可以在多线程中同时执行。但是,在使用共享变量时,或者在执行文件输出或者绘制图等的情况下,多线程同时执行就可能得到奇怪的结果。
    例如,使用整数全局变量totalNumber来累加处理的数据的个数,为了执行下面的加法计算,在多线程环境中执行该方法会得到什么结果呢?

    - (void)addNumber:(NSInteger)n
    {
        totalNumber +=n;
    }

    当两个线程同时执行的情况下,当然,在OS功能支持下,线程在运行的过程中会时而得到CPU的执行权,时而被挂起执行权,因此整体上看,执行结果会偏离了我们期待的结果,原因是值的读取、更新、写入操作被多线程同时执行了。

    二、锁

    为了使多个线程间可以相互排斥地使用全局变量等共享资源,可以使用NSLock类。
    锁具有每次只允许单个线程获得并使用的性质。获得锁称为“加锁”,释放锁称为”解锁“。
    锁使用类方法alloc和初始化器init来创建并初始化。但是,锁应该在程序开始多线程执行前创建。

    NSLock * countLock = [[NSLock alloc]init];

    获得锁的方法和释放锁的方法都在协议NSLocking中定义。

    -  (void) lock;//如果锁正被使用,则线程进入休眠状态;如果锁没被使用,则锁的状态变为正被使用,线程继续执行。
    -  (void) unlock;//将锁设置为没被使用,此时如果有等待该锁资源的正在休眠的线程,则将其唤醒。

    在该代码中,从某线程执行1取得锁到该线程执行2释放锁期间,其他线程在执行1时将进入休眠状态,不能执行临界区代码。锁被释放后,在执行1时在休眠的线程中选择一个线程,在该线程取得锁后进入临界区执行。

    - (void)addNumber:(NSInteger)n
    {
      [aLock lock];-----------------------1
      totalNumber +=n;          //临界区
      [aLock unlock];--------------------2
    }

    某个锁被lock后,必须执行一次unlock。而且lock和unlock必须在同一个线程执行。

    三、死锁

    线程和锁的关系必须在设计之初就经过仔细的考虑。如果错误地使用锁,不但不能按照预期执行互斥,还可能使多个线程陷入到不能执行的状态,即死锁(deadlock)状态。


    线程1

    线程2


    线程1占有文件A并正在处理,途中有需要占有文件B。而另一方面,线程2占有文件B,途中有需要占有文件A。线程1为了处理文 件B想要获得锁bLock,但是它已经被线程2获得。同样,线程2想要获得的锁aLock也被线程1占有着。这种情况下,线程1和线程2就会同时进入休眠 状态,而且双方都不能跳出该状态。

    四、尝试获得锁

    NSLock类不仅能获得锁和释放锁,还有检查是否获得锁的功能。利用这些功能,就可以在不能获得锁时进行其他处理。

    -  (BOOL) tryLock

    用接收器尝试获得某个锁,如果可以获得该锁则返回YES,不能获得时,与lock处理不同,线程没有进入休眠状态,而且直接返回NO并继续执行。

    五、条件锁

    NSConditionLock实例初始化,设置参数condition指定的值。NSConditionLock的指定初始化器。

    - (instancetype)initWithCondition:(NSInteger)condition

    此时返回锁中设定的值

    @property (readonly) NSInteger condition;

    如果锁正在被使用,则线程进入休眠状态。
    锁不在被使用时,如果锁值和参数condition的值一致,则将锁的状态改为正在被使用,然后继续执行,如果不一致,则线程进入休眠状态。

    - (void)lockWhenCondition:(NSInteger)condition;

    在锁中设置参数condition指定的值。将锁设置为不在使用,此时如果有等待获得该锁且处于休眠状态的线程,则将其唤醒。

    - (void)unlockWithCondition:(NSInteger)condition;

    尚未使用锁且锁值与参数condition相同时,获得锁并返回YES,不能获得锁时也不进入休眠状态,而是返回NO,线程继续执行。

    - (BOOL)tryLockWhenCondition:(NSInteger)condition;

    使用方法lock、unlock、trylock时都可以获得锁和释放锁,而且不用关心锁的值。

    六、NSRecursiveLock,递归锁

    某线程获得锁后,到该线程释放锁期间,想要获得该锁的线程就会进入休眠。使用NSLock的锁时,如果已经获得锁的线程在没有释放它的情况下还想再次获得该锁,该线程也会进入休眠。但是,由于没有从休眠状态唤醒的线程,所以这就是死锁。

    [aLock lock];
    [aLock lock];//这里发生死锁
    [aLock unlock];
    [aLock unlock];

    解决这种情况可以使用NSRecursiveLock类的锁,拥有锁的线程即使多次获得同一个锁也不会进入死锁。但是,其他线程当然也不能获得该锁。获得次数和释放次数一致时,锁就会被释放。

  • 相关阅读:
    bzoj 1176 cdq分治套树状数组
    Codeforces 669E cdq分治
    Codeforces 1101D 点分治
    Codeforces 1100E 拓扑排序
    Codeforces 1188D Make Equal DP
    Codeforces 1188A 构造
    Codeforces 1188B 式子转化
    Codeforces 1188C DP 鸽巢原理
    Codeforces 1179D 树形DP 斜率优化
    git commit -m "XX"报错 pre -commit hook failed (add --no-verify to bypass)问题
  • 原文地址:https://www.cnblogs.com/zhengju/p/5968981.html
Copyright © 2011-2022 走看看