iOS中的锁和线程同步

线程同步和iOS中的锁
一、自旋锁(OSSpinLock)

  1. 一直占用cpu,相当于while循环
  2. 已经不被推荐,因为可能会产生优先级反转;

优先级反转

  1. 多任务的调度有许多算法,常见的有FIFO、优先级、时间片轮询、短任务优先;
  2. 系统一般采取混合算法,因为不同进程特点不一样,单一的调度算法不能满足所有的场景;比如短任务算法需要在线程之间的切换消耗 和 任务真正执行的消耗,两者之间平衡,所以,每种调度算法都有自己的缺点和有点,混合算法是最高效的;
  3. 低等级的任务在访问资源后使用自旋锁进行加锁,然后,高等级任务访问同一个资源,因为该资源被加锁,所以高等级任务等待锁的释放之后才能继续工作。此时高等级任务一般会释放 cpu 进入休眠。此时,一个中等级的任务开始执行且不需要访问被加锁的资源,此时因为中等级的优先级更高,所以直接抢占了低等级任务的 CPU。此时,低等级的任务只能处于 ready 状态等待 CPU 分配,而高等级的任务在等待自旋锁的释放,处于休眠状态。此时就出现了优先级反转,即:高等级的任务被分配的 CPU 低于中等级。
  4. 以上是高等级任务在等待时可以休眠的情况,也就是等待不是很占用cpu,这种情况下只是出现优先级反转,但是如果高优先级任务的等待是忙循环,也就会出现cpu把事件都分配给高级别任务,但是高级别任务又是忙循环,低级别任务无法获取cpu时间,最终就是死循环,系统可能会崩溃;
  5. 另外,有一些机制会检查高等级的 CPU 分配时间,如果一定时间内高等级的任务无法得到 CPU 时间片,则会认为系统除了故障,可能会进行重启。宇航局的火星车,就是因为有高优先级的线程被压制,从而在指定时间内无法获得 CPU,导致 “看门狗”认为系统出了无法恢复的故障,直接重启了系统。重启后系统再次进入相同状态,导致不断重启,无法正常工作。

二、os_unfair_lock

  1. 休眠,不是忙等待,是互斥锁;
  2. iOS10之后才能使用,用于替换自旋锁;

setProperty 中如果添加了 automic 关键字,则会使用互斥锁进行加锁:

image.png

这里的 spinlock_t 原来是自旋锁,但是其实现方式已经被替换成了互斥锁,只不过名称没有改过来而已:

image.png

三、pthread_mutex

  1. 跨平台;
  2. 互斥锁,等待属于休眠;
  3. 可以设置类型,有:默认(PTHREAD_MUTEX_NORMAL)、递归所(PTHREAD_MUTEX_RECUSIVE)、错误检查锁(PTHREAD_MUTEX_ERRORCHECK);
  4. normal类型的锁在创建时(pthread_mutex_init(&mutex,0))传空即可,另外两种锁需要使用pthread_mutex_attr_settype来设置;
  5. 需要使用destory进行销毁,attr、condition、mutex都需要销毁;
  6. 递归锁在递归时使用不会造成死锁问题;
  7. 递归锁:允许同一个线程对通一把锁重复加锁,加锁次数和解锁次数对应时才能解锁;
  8. 条件锁:pthread_cond_wait 和 pthread_cond_signal 配合使用;pthread_cond_wait(cond,mutex)会标记cond并解锁mutex,线程进入休眠,并且阻塞代码。当调用pthread_cond_signal(cond)之后,接收到信号后再对mutex进行加锁,然后继续执行代码;
  9. 条件所中的cond并不是类似于if else,而只是一个标记;
  10. 条件所一般用于解决线程以来问题,当一个线程任务的执行需要以来另一个线程任务的执行时使用,条件起到的是标记和传递的作用;
  11. 唤醒一个线程的等待使用pthread_cond_signal,唤醒所有等待的线程则使用广播方法:pthread_cond_broadcast
  12. 条件等待时,调用signal方法发送信号之后,wait方法其实不是只判断一次的,而是循环判断检测,如果这个锁没加锁成功,就继续等待,如果加锁成功就返回,继续执行后面的代码;所以,signal和unlock方法的调用顺序差别不大,只有在两者调用期间出现很大的延时操作时,wait方法会等待很久才加锁成功,继续执行后面代码。总之,逻辑不会受影响,只是时间问题,pthread_mutex内部已经考虑到这种问题,完成了解决方案的实现;

sqlite3 中也会使用到互斥锁。sqlite3 为了效率和安全的平衡提供几种模式:单线程模式下不加锁,默认只能在单线程情况下使用。串行模式下因为全量加锁,所以在多线程下使用时依然安全。还有一个是多线程模式,部分地方进行了加锁,部分地方禁止了锁,属于一种折中方案:

image.png

四、自旋锁和互斥锁

  1. step:按行执行代码;si:按行执行汇编代码;
  2. 自旋锁:忙等,相当于while循环,在汇编中的体现是:jne -> 0xxxxxx ,条件成立则跳转到指定地址循环执行;
  3. 互斥锁:等待时休眠,不占用cpu,类似于RunLoop;本质是:syscall,调用系统级别的函数去休眠并等待被唤醒的消息;
  4. OSSpinLoc是自旋锁,os_unfair_lock和pthread_mutex是互斥锁;

五、NSLock、NSRecusiveLock、NSCondition、NSConditionLock

  1. 是对pthread_mutex的封装,封装成了OC对象;

六、串行队列

  1. async和sync决定是否可以开启线程,队列表示任务的执行方式。
  2. 任务是一代码块为最小颗粒,而不是以一行代码为最小颗粒。代码块中的每一行代码默认都是同步执行;
  3. sync表示从队列中取出任务到当前线程执行,async表示从队列中取出任务异步执行。所以,如果sync+serial queue 结果就是:可以将需要同步的任务(抢占资源的任务)添加到同一个queue中然后同步执行,结果就是线程同步成功;

七、dispatch_semaphore

  1. dispatch_semaphore在初始化时需要使用create传入一个最大信号量;
  2. 最大信号量意味着最多又多少条线程可以同时执行,如果是1,那么就类似于串行队列,所以也可以实现线程同步

dispatch_semaphore_wait(semaphore,time)的作用:

  1. 当semaphore>0时,让当前线程直接继续执行,且dispatch_semaphore的值-1;
  2. 当semaphore<=0时,让当前线程阻塞在这句代码中,且让线程休眠等待;
  3. time表示如果需要等待,那么最长等多久需要返回,传now立马进行判断,传forever就一直等;

dispatch_semaphore_signal(semaphore)的作用:

  1. 让semaphore+1;

使用流程:

  1. 比如创建semaphore设置的最大信号量是5,有100个线程会执行到这句代码;
  2. 当第一次进入时,semaphore=5>0,所以就会让当前进入,继续执行并且dispatch_semaphore-1=4;
  3. 假设dispatch_semaphore_wait和dispatch_semaphore_signal之间的代码时耗时操作;
  4. 以此类推,当第五条线程进入之后,semaphore=0,而因为是耗时操作,所以100条线程都会执行到dispatch_semaphore_wait代码;
  5. 从第六条线程开始,因为dispatch_semaphore=0,线程被阻塞,剩余的95条线程如果在耗时操作没有完成之前,也就是没有调用dispatch_semaphore_signal之前,如果执行到dispatch_semaphore_wait,那么都会处于休眠等待的状态;
  6. 当最先进入的线程执行完耗时操作,调用dispatch_semaphore_signal,dispatch_semaphore+1>0,那么第六条线程就会从休眠中唤醒,继续执行后面的代码;
  7. 以此类推…所以当semaphore初始化时设置为1,那么只有一条线程能够访问共享资源,如此便实现了线程同步;

八、@synchronized

  1. 底层仍然是pthread_mutex,是使用的recusive创建的递归锁;
  2. 根据@synchronized(obj){}传入的对象,生成一个和对象对应的一把锁,存入到map表中;
  3. 关键字不能自动提示,因为存在性能问题,不被推荐使用;

九、性能对比

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