GCD - 死锁 [ 转载 ]

转载的一位大牛的GCD死锁分析感觉很不错,在此分享(http://www.cnblogs.com/LDSmallCat/p/4910080.html)

一.首先来看这段 正确的 代码:
在touchesbegan中调用test方法,可以看到如下打印

827473-20151026001309333-1906771235.png

分析一下原理:
1>调用test方法执,行到25行时没有开启线程,在主线程中打印
2> 执行到31行时检测到异步函数async,此时async函数直接返回,不会等函数中的block1执行完毕就返回,执行到31行系统有两件事要做,第一跳至42行打印,第二开启一个新的线程1(是子线程而非主线程)执行block1当中的内容.所以看到主线程0,4先打印,打印结束就没有主线程的事情了,接下来子线程登场.()
3> 子线程1执行block1的内容:
3.1执行33行打印 在(子线程1) 打印
3.2执行到35行检测到async异步函数,直接返回,不等待block2执行完毕,程序继续向下执行,即打印39行 3 子线程打印,同时又创建一个 (子线程2)
4> ok 到这里我们还需要分析一下,程序运行到这里我们一共经历了几个线程?三个!主线程一个,31行 35行分别创建了两个子线程1和2
(怎么看是否创建了新的线程?"一般"来说看函数,是async异步执行还是sync同步执行,为什么要用"一般"?往下看)
那么问题来了1> 33行和 39行的打印(即1,3打印)都在一个线程上我们可以理解(看线程地址),但是为什么36行的打印 也和这两个 在 同一个线程呢? 因为31行创建了一个子线程 打印了33行和39行之后,这个线程的任务结束了,正常来说就要销毁了,没他什么事了.但是我们在35行又需要创建一个线程,创建线程需要时间和空间成本(占用内存)所以在子线程1执行完任务进入线程池要销毁的时候,发现系统还要再创建一个线程,ok那我就不销毁了,你也不用再创建了,所以线程1就被重复利用了,所以他们在一个线程上打印.关于为什么在同一个子线程上打印,我给出的只是一个理解的方式,是不是准确的?我不确定.因为线程很多的情况十分复杂,我们这个事例程序很简单,可以这样理解.所以我前面说的是有问题的,这个程序只经历了两个线程,主线程和同一个子线程
5>ok我们回来 在子线程1执行33 39行的打印之后,被系统重复利用,来执行35行block的内容,36行打印结束一个消息循环结束,进入休眠等待下次触摸屏幕.
6>总结:为什么程序的打印结果会是这样的?与两个因素有关1>函数 是async异步执行还是sync同步执行2>队列我们创建的是串行队列这个串行队列里面有两个任务 block1 和block2,串行队列遵循fifo原则:first in first out,就先进先出原则,blokc1在31行加入,先执行,block2在35行加入后执行.block1中有三个任务33行39行打印和block2,且block2在两个打印中间,按照程序自上到下执行…尼玛问题又来了,为什么block2不是在两个打印任务之间执行?而是在打印结束后执行?ok 这时就要看函数啦~async 这是什么?异步执行!!要开启新的线程(或是重复利用线程池中已经创建的线程,什么样的线程可以重复利用?这个线程已经执行完它的任务,进入线程池马上销毁的线程才可以重复利用)(开启线程或是获取线程池中重复利用的线程是需要时间成本的)检测到这个函数怎么办?直接返回,不用等这个函数的block执行完就返回.什么意思?执行到35行的时候兵分两路,一路向下执行39行打印,一路从线程池中获取重复利用的线程处理block2的任务.在兵分两路的时候子线程以还没有处理完39行的打印,它的任务没有结束啊!为什么处理block2没有创建新的线程?因为需要时间和控件成本.GCD为我们做了优化,(怎么优化的我们不用操心~看不到源码这是个迷,不过看到了也不一定看得懂!哈哈所以不创建新的线程,等子线程1执行完他的任务,我们重复利用它!ok说完了,

二.了解了上图,我们再来看下面的代码


827473-20151026001405974-1989316509.png

我把35行的函数换成了sync同步执行函数,看打印结果,2.3没有打印!这就是传说中的死锁.什么意思?36 ,39行没有打印,程序在等待,卡住了.不能正常向下执行了!

分析一下原因:

1>点击屏幕调用test方法,主线程打印25行42行,主线程不用我们创建.系统自动创建.上面已经提到原理就不再赘述,直接来重点

2>31行代码做了这几件事:1将block1任务添加到队列串行queue中,2>利用async函数开启新的子线程1处理block1任务.

3>当执行到35行代码时做了这几件事情:1>将block2添加到串行队列queue中,2>sync函数不会开启新的线程,只能在当前线程执行任务

4>ok现在我们分析一下是什么情况:1>queue当中有两个任务,block1和block2.2>只有一个线程:子线程1,在31行创建的.queue串行执行任务,要等block1执行完才能执行block2任务.啥意思?31行创建的子线程1要执行完block1才能执行block2.执行到35行block2任务的时候要等待block1执行完,可是程序自上而下执行,尼玛block2不执行完block1就不会执行完!你懂了么?block2所在的是sync函数不能开启新的线程,只能和block1共用一个线程,这个线程在处理block1,现在的问题是没有线程处理block2的任务~!!!所以就出现了传说中的死锁!别尼玛问我为啥主线程不处理block2的任务!我会让你的老板开除你的!还没懂?最后说一遍:程序自上而下执行知道吧?那么block1执行完时不时需要block1里面的所有代码都执行完啊?可是到了35行block2没有开启新的线程,只能和block1共用一个线程,现在这个线程在处理block1的任务,要等block1执行完才能执行block2的任务.所以程序就卡住了.ok还不懂?看上面正确的代码第35行我们呢用的是async函数,它能开启一个全新的线程来处理block2任务,且不用等到block2执行完这个函数就返回了!函数返回啥意思----就是继续向下执行了.ok说完了.别告诉我你还没

三:有了上面的分析相信小伙伴们有了一定的了解,再来分析一下下面的代码是不是死锁?
所在哪里了?


827473-20151025204741380-1238287898.png

死锁,锁在64行的代码.
分析一下原因:
1>主线程执行47, 71行打印之后进入 休眠状态等待下次点击屏幕,在两个打印之间检测到54行async不等block1执行完继续向下.ok没问题
2>54行将block1加入到queue队列,开async函数开启新的线程执行block1,block1任务代码书讯执行 56行打印
3>58行检测到async函数不等block2执行完继续向下执行,62行打印,
4>执行到64行 sync函数 没有开启新的线程,要和block1共用一个线程,造成死锁
5>分析一下现在什么情况:queue中有3个任务,block1,block2,block3,顺序执行,为什么卡在64行?不是58行?58行开启了一个新的线程来处理block2,所以不是这里.block3没有开启线程要用别人的线程,用谁的呢?你看它在哪个block里.她在block1里面,block1在54行开启了线程正在处理block1,所以现在block2没人处理.就造成死锁.
6>那么为什么block2没有打印呢?有线程处理他的任务啊?这是要看队列queue,三个任务顺序执行,block1执行完了么?没有,所以block2不会打印.ok说完了!有疑问?block2?好吧 来看下面代码.我把64行的函数换了.


827473-20151025204817349-1738359650.png

为什么是这个打印顺序呀?
1>主线程优先处理,所以 0,6先打印,
2>为什么135这个顺序打印丫?135在同一个线程中,即block1的线程中,代码顺序执行,所以这个顺序
3>为什么2,4打印不是穿插在1,3,5之间呢?1>因为2打印要开启新的线程,有时间成本,啥意思,就是我开启一个线程需要时间.所以他要晚
2>135在主线程,优先于子线程处理任务,优先级就比子线程高,所以它晚
3>async函数不等待后面block执行完就向下执行,所以它晚,
4>为什么2,4打印没有开启两个线程而是一个线程,因为开启新的线程需要时间和控件成本GCD为我们自动做了优化,重复利用了线程池中执行完本职任务即将销毁的线程.
5>为什么24的打印顺序是这样:1>因为4利用了2的线程,(看线程地址)代码自上而下执行,queue中block2排在block3之前,所以要block2打印完才能打印block3.

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

推荐阅读更多精彩内容

  • 从哪说起呢? 单纯讲多线程编程真的不知道从哪下嘴。。 不如我直接引用一个最简单的问题,以这个作为切入点好了 在ma...
    Mr_Baymax阅读 2,757评论 1 17
  • GCD GCD简介 Grand Central Dispatch中枢调度器 纯C语言的,提供了非常强大的函数 优势...
    彼岸的黑色曼陀罗阅读 439评论 0 0
  • 多线程 线程是进程内部执行任务的一种途径,多线程技术能适当提高程序执行效率和资源利用率,iOS 中的多线程技术主要...
    Yasic阅读 235评论 0 1
  • 多线程 在iOS开发中为提高程序的运行效率会将比较耗时的操作放在子线程中执行,iOS系统进程默认启动一个主线程,用...
    郭豪豪阅读 2,597评论 0 4
  • 在每个人的一生中都可能会有一段独居的生活。也许是在在北上广独自打拼的日日夜夜,也许是婚姻围墙里的精神独居,也许是步...
    侯七鸭阅读 703评论 0 1