关于GCD中的队列、线程、任务之间的关系及死锁的理解

1. 任务与队列之间的关系

  • 任务产生后被加入到某个队列中,队列仅保管任务,不执行任务;
  • 队列分为串行队列和并行队列两种,无论是串行队列还是并行队列都遵守先进先出的原则;
  • 对于串行队列,要派发位于队首的任务,必须等待上一个派出的任务被执行完毕;即从串行队列派出的任务,同一时刻,最多只有一个任务被执行;
  • 对于并行队列,要派发位于队首的任务,无需等待上一个派出的任务被执行完毕;即从并行队列派出的任务,同一时刻,可以有多个任务被执行;

2. 任务与线程之间的关系

  • 任务由线程执行,同一时刻,一个线程只能执行一个任务;要执行下一个任务,上一个任务必须执行完成(或被暂停、取消);
  • 任务分为同步执行任务和异步执行任务两种;
  • 对于同步执行任务A,产生任务A的线程将任务交给队列后,会暂停执行自己正在执行的任务B,一直等待任务A被执行完成后,自己才会继续执行任务B;
  • 对于异步执行任务C,产生任务C的线程将任务交给队列后,不会暂停执行自己正在执行的任务B,不会等待任务C被执行完成,自己会不间断的执行任务B;

3. 队列与线程之间的关系

  • 串行队列中的所有任务只会有一个在被执行,因此同一时刻,串行队列最多只会对应一个线程;
  • 并行队列中的任务可能有多个同时在被执行,因此同一时刻,并行队列可能会对应多个线程;

4. 队列、任务、线程之间的关系

  • 串行队列中的同步任务A,会有产生任务A的线程T执行,因为线程T在产生任务A后即暂停正在执行的任务B,任务A被队列派出后需要有一个线程执行,而此时线程T一定处于空闲状态,与其新开一个线程,不如利用该处于空闲的线程T。即任务A由该线程T在暂停执行B的时间段内执行,然而该线程T并不一定是暂停B后立即执行A,要等待A所在的队列将A派发出才能开始执行,这个队列会等待前一个派发出的任务执行完成之后才会派发A,因此线程T在执行任务A之前可能会等待一段时间;
  • 串行队列中的异步任务A,由于产生任务A的线程T1不会等待A被执行完成,因此A被队列派发出后,线程T1不一定为空,因此A会被交给一个新的线程T2去执行,该线程T2可能会执行该串行队列派出的下一个异步任务B,但不一定,特别是异步任务A和异步任务B之间有一个同步任务C的情况下,C不由线程T2执行,C被执行的这段时间内线程T2可能会接到新任务,派发出异步任务B时T2正在执行任务,任务B就会被派发到其他线程;
  • 并行队列中的同步任务A与在串行队列中类似,也有产生任务A的线程T执行,不同的是,该任务A可能会被所在的队列立即派出,因为并行队列派出新任务无需等待之前派出的任务执行完成,但是,若该队列中有很多任务还未派出,任务A也要等待从队尾移动到队首之后才会被派出;
  • 并行队列中的异步任务A,异步任务B,异步任务C一般会被交由不同的新线程执行,因为队列派出任务B时,产生任务A的线程和执行任务A的线程都不一定为空闲状态,而任务B可以被立即执行,因此只要系统还可以产生新线程,就会产生一个新线程来执行任务B,当然系统产生新线程的能力有限,若任务过多,不能产生足够多的新线程来执行任务,那么未派出的任务也会等待有空闲线程时才会被派出。

5. 主队列和主线程

  • 主队列是串行队列;
  • 与普通串行队列不同的是,主队列中的所有任务都由主线程执行,将异步任务加入主队列也不会开启新线程;

6. 常见的死锁

直接上代码:

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_queue_t serialQueue = dispatch_queue_create("jason_serial_queue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(serialQueue, ^{
        NSLog(@"1");
        //串行队列同步任务中嵌套同步任务会形成死锁
        dispatch_sync(serialQueue, ^{
            NSLog(@"2");
        });
        NSLog(@"3");
    });

    dispatch_async(serialQueue, ^{
        NSLog(@"4");
        //串行队列异步任务中嵌套同步任务会形成死锁
        dispatch_sync(serialQueue, ^{
            NSLog(@"5");
        });
        NSLog(@"6");
    });

    //主队列正在执行的任务中嵌套同步任务会形成死锁
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"sync at mainQueue");
    });
 }

一句话总结,在串行队列中的任务中嵌套同步任务。

原因分析:
  • 内层的同步任务要等待所在串行队列上次派发出的任务执行完成才能被派发;
  • 所在串行队列上次派发出的任务就是正在执行的外层任务;
  • 正在执行的外层任务已暂停等待内层任务执行完成;
  • 由于外层任务未完成,内层任务不会被派发;
  • 内层任务不会被派发就不能被执行完成,不能执行完成外层任务就不能继续,死锁产生。
  • 主队列是串行队列,主线程正在执行的任务是串行队列中的任务,故在向主队列中加入同步任务会形成死锁。要想在主线程中进行同步任务,请将其加入到其他队列。

7. 简单测试

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_queue_t serialQueue = dispatch_queue_create("jason_serial_queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQueue, ^{
        NSLog(@"serialQueue1");
        sleep(2);
        NSLog(@"serialQueue2");
        sleep(2);
        NSLog(@"serialQueue3");
        NSLog(@"thread1:%@", [NSThread currentThread]);
    });
    NSLog(@"mainQueue1");
    
    dispatch_async(serialQueue, ^{
        NSLog(@"serialQueue4");
        NSLog(@"serialQueue5");
        NSLog(@"serialQueue6");
        NSLog(@"thread2:%@", [NSThread currentThread]);
    });
    NSLog(@"mainQueue2");
    
    dispatch_sync(serialQueue, ^{
        NSLog(@"serialQueue7");
        sleep(2);
        NSLog(@"serialQueue8");
        sleep(2);
        NSLog(@"serialQueue9");
        NSLog(@"thread3:%@", [NSThread currentThread]);
    });
    NSLog(@"mainQueue3");

    NSLog(@"thread4:%@", [NSThread currentThread]);
}
问题:
  1. mainQueue1、mainQueue2、mainQueue3三者的输出顺序是是否确定?
  2. serialQueue4会不会出现在serialQueue1、serialQueue2、serialQueue3中间?
  3. serialQueue1、serialQueue2一直到serialQueue9的输出顺序是否确定?
  4. mainQueue1可能出现在serialQueue1、serialQueue2一直到serialQueue9中间的哪些位置?
  5. thread1、thread2、thread3、thread4之间有什么关系?
答案:
  1. 一定按照mainQueue1,mainQueue2,mainQueue3的顺序输出,这个就不用解释了。
  2. 不会。因为对于串行队列,上一个派发出的任务完成之前不会派发新的任务,serialQueue3输出之前,serialQueue4所在任务会持续在serialQueue内等待。
  3. 一定按照serialQueue1、serialQueue2、serialQueue3、serialQueue4、serialQueue5、serialQueue6、serialQueue7、serialQueue8、serialQueue9的顺序输出。
  4. mainQueue1可能出现在serialQueue4之前的任何位置,但由于有个sleep(2),正常情况下只会出现在serialQueue2之前,serialQueue1前后的两个位置。
  5. thread1、thread2都不是主线程,(由于两个异步任务在等待前一个异步任务执行完成,所以很可能为同一线程;若前一个异步任务结束与后一个异步任务开始的时间间隔较大,比如中间还有一个同步任务,或前一个异步任务执行结束时后一个异步任务还未入队,两个线程为同一线程的可能性就没那么大了);thread3、thread4为主线程。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容