gcd多线程任务与队列组合分析

关于gcd中串行队列并行队列,以及同步任务和异步任务的花式嵌套,分析执行结果

多线程调试常用代码:

[NSThread sleepForTimeInterval:3.0f];  //模拟耗时操作
[NSThread currentThread];              //%@打印当前线程序号,主线程师1,一次递增。

gcd的任务

同步任务: dispatch_sync(queue, ^{});

  • 最大的作用是阻塞并行队列的非嵌套异步任务执行。只有当前的同步任务执行完成,才会执行后面的同步/异步任务。
- (void)demo01 { //该代码下方嵌套同步任务一定先用嵌套异步任务执行
    dispatch_queue_t concurrent = dispatch_queue_create("com.gcd.example", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(concurrent, ^{
        //跑线程2
        NSLog(@"异步任务000 %@", [NSThread currentThread]);
        
        //同步任务
        dispatch_sync(concurrent, ^{
            //跑线程2
            NSLog(@"嵌套同步任务 %@", [NSThread currentThread]);
            //模拟耗时操作
            [NSThread sleepForTimeInterval:3];
        });
        
        //异步任务
        dispatch_async(concurrent, ^{
            //跑线程2/3/...
            NSLog(@"嵌套异步任务111 %@", [NSThread currentThread]);
        });
        
        //异步任务
        dispatch_async(concurrent, ^{
            //跑线程2/3/...
            NSLog(@"嵌套异步任务222 %@", [NSThread currentThread]);
        });
        
        //异步任务
        dispatch_async(concurrent, ^{
            //跑线程2/3/...
            NSLog(@"嵌套异步任务333 %@", [NSThread currentThread]);
        });
        
        //异步任务
        dispatch_async(concurrent, ^{
            //跑线程2/3/...
            NSLog(@"嵌套异步任务444 %@", [NSThread currentThread]);
        });
    });
}
//执行结果:
多线程篇[13337:19767072] 异步任务000 {number = 2, name = (null)}
多线程篇[13337:19767072] 嵌套同步任务 {number = 2, name = (null)}
多线程篇[13337:19767072] 嵌套异步任务222 {number = 2, name = (null)}
多线程篇[13337:19767075] 嵌套异步任务111 {number = 3, name = (null)}
多线程篇[13337:19767085] 嵌套异步任务333 {number = 5, name = (null)}
多线程篇[13337:19767087] 嵌套异步任务444 {number = 4, name = (null)}
  • 不会开启新的线程,在当前线程执行任务
  • 在主线程中会阻塞当前任务(原因:主线程runLoop一直在执行中,再次添加任务互相等待,导致阻塞)不会执行后续代码
- (void)demo02 {  //主队列同步任务阻塞
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    dispatch_sync(mainQueue, ^{
        NSLog(@"同步任务%@", [NSThread currentThread]);   //阻塞
    });
    
    NSLog(@"当前任务%@", [NSThread currentThread]); //阻塞
}
//原因, 在mainQueue中,是有一个runLoop一直循环执行当前的任务,添加同步任务后,同步任务去等待这个不会结束的任务,主线程的runLoop不会结束,互相等待导致阻塞

问题1:为何主队列添加同步任务会死锁, 而串行队列添加同步任务不会死锁?
    
为何主队列添加同步任务会死锁? 
本质:在一个队列中跑两个互相依赖的任务。  
分析:如上代码:主队列是在主线程中运行,这里的demo02 和 sync的同步任务都在主队列分配的主线程中去执行, demo02任务还没执行结束,内部sync任务已经来了,执行条件却是需要demo02执行结束,两者互相等待,死锁。

串行队列添加同步任务不会死锁? 
本质:任务跑在两个队列中mainQueue和serialQueue。
分析:而在串行队列当中,如下代码:例如demo03被放在主线程主队列当中执行,然后同步任务被放在串行队列serialQueue当中执行(没有开辟新的线程,所以任务都是主线程去执行,当主线程空闲的时候就去分别执行队列中的任务),不会互相抢占资源。

- (void)demo03 {  //串行队列同步任务不阻塞
    dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.serial", NULL);
    
    dispatch_sync(serialQueue, ^{
        NSLog(@"同步任务%@", [NSThread currentThread]);  
    });
    NSLog(@"当前任务%@", [NSThread currentThread]); 
}
//执行结果:
多线程篇[15215:19872610] 同步任务{number = 1, name = main}
多线程篇[15215:19872610] 当前任务{number = 1, name = main}


问题2:为何串行队列同步任务嵌套同步任务会阻塞
- (void)demo04 { //嵌套串行队列阻塞
    dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.serial", NULL);
    
    dispatch_sync(serialQueue, ^{
        NSLog(@"同步任务11%@", [NSThread currentThread]);
        dispatch_sync(serialQueue, ^{
            NSLog(@"嵌套同步任务22%@", [NSThread currentThread]);  //阻塞
        });
    });
    NSLog(@"当前任务%@", [NSThread currentThread]);  //阻塞
}
//同主队列串行任务阻塞一样,任务11和任务22都被放在同一个队列分配给主线程执行,互相依赖

问题3:为何串行队列异步任务嵌套了同步任务会阻塞
- (void)demo05 {
    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    NSLog(@"开始 - %@", [NSThread currentThread]); //跑在线程1
    
    dispatch_async(queue, ^{
        //跑在线程2
        NSLog(@"异步任务前段 - %@", [NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
        //跑在线程2,但是当前串行队列当中的上一个任务还没跑结束,gg
        dispatch_sync(queue, ^{
            NSLog(@"同步任务 - %@", [NSThread currentThread]);  //阻塞
        });
        
        NSLog(@"异步任务后段 - %@", [NSThread currentThread]);  //阻塞
    });
    NSLog(@"结束 - %@", [NSThread currentThread]);
}
//执行结果:
多线程篇[17909:20024157] 开始 - {number = 1, name = main}
多线程篇[17909:20024157] 结束 - {number = 1, name = main}
多线程篇[17909:20024192] 异步任务前段 - {number = 2, name = (null)}
(lldb) 
//同上,虽然异步任务开启了新的线程number=2, 但是新线程同时执行两个任务 async和内部嵌套的sync,互相依赖

  • 在串行队列中,无论什么任务只要内部嵌套了同步任务就会阻塞。
//注意:
//并行队列中,同步任务嵌套了同步任务,不阻塞
- (void)demo06 {
    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"开始 - %@", [NSThread currentThread]); //跑在线程1
    
    dispatch_sync(queue, ^{
        //跑在线程1
        NSLog(@"同步任务11前段 - %@", [NSThread currentThread]);
        
        //跑在线程1  
        dispatch_sync(queue, ^{
            [NSThread sleepForTimeInterval:3];
            //分析:既然是并行队列那么任务11和任务22被添加到队列当中,不需要等待某个任务执行完成才可执行后边的任务,且一定是按照代码顺序执行。先执行11,执行到一半需要执行任务22,先搁置11,去执行任务22, 执行完22再去执行11。
            NSLog(@"同步任务22 - %@", [NSThread currentThread]);
        }); 
        NSLog(@"同步任务11后段 - %@", [NSThread currentThread]);
    });
    NSLog(@"结束 - %@", [NSThread currentThread]);
}
//执行结果
多线程篇[18897:20066510] 开始 - {number = 1, name = main}
多线程篇[18897:20066510] 同步任务11前段 - {number = 1, name = main}
多线程篇[18897:20066510] 同步任务22 - {number = 1, name = main}
多线程篇[18897:20066510] 同步任务11后段 - {number = 1, name = main}
多线程篇[18897:20066510] 结束 - {number = 1, name = main}



//并行队列中,异步嵌套了同步任务,不阻塞
- (void)demo07 {
    dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"开始 - %@", [NSThread currentThread]); //跑在线程1
    
    dispatch_async(queue, ^{
        //跑在线程2
        NSLog(@"异步任务前段 - %@", [NSThread currentThread]);
  
        //同步任务 分析同上 异步队列同步任务嵌套同步任务
        dispatch_sync(queue, ^{
            [NSThread sleepForTimeInterval:3];
            NSLog(@"同步任务 - %@", [NSThread currentThread]);
        });
        NSLog(@"异步任务后段 - %@", [NSThread currentThread]);
    });
    
    NSLog(@"结束 - %@", [NSThread currentThread]);
}
//执行结果:
多线程篇[18384:20049195] 开始 - {number = 1, name = main}
多线程篇[18384:20049195] 结束 - {number = 1, name = main}
多线程篇[18384:20049509] 异步任务前段 - {number = 2, name = (null)}
多线程篇[18384:20049509] 同步任务 - {number = 2, name = (null)}
多线程篇[18384:20049509] 异步任务后段 - {number = 2, name = (null)}

异步任务:dispatch_async(queue, ^{});

  • 会开启新的线程,开启线程数取决于队列情况(串行队列开启一个,并行队列开启多个)

gcd的队列

串行队列:dispatch_queue_t serialQueue = dispatch_queue_create("com.example.gcd.serial", NULL);

  • 参数二: DISPATCH_QUEUE_SERIAL或者NULL均为串行队列
  • 无论任务类型,一定是顺序执行
  • 串行队列最多只能开启两条线程(算上当前所在线程,在添加异步任务的时候),且异步任务一定是子线程去执行
//串行队列最多只能开启两条线程
- (void)demo08 {
    dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.serial", NULL);
    
    dispatch_async(queue, ^{
        NSLog(@"异步任务1%@", [NSThread currentThread]);
        dispatch_async(queue, ^{
            NSLog(@"异步嵌套任务1%@", [NSThread currentThread]);
            dispatch_async(queue, ^{
                NSLog(@"异步嵌套嵌套任务1%@", [NSThread currentThread]);
            });
        });
    });
    dispatch_async(queue, ^{
        NSLog(@"异步任务2%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"异步任务3%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"异步任务4%@", [NSThread currentThread]);
    });
    NSLog(@"当前任务%@", [NSThread currentThread]);
}
//执行结果
多线程篇[19670:20107037] 当前任务{number = 1, name = main}
多线程篇[19670:20107068] 异步任务1{number = 2, name = (null)}
多线程篇[19670:20107068] 异步任务2{number = 2, name = (null)}
多线程篇[19670:20107068] 异步任务3{number = 2, name = (null)}
多线程篇[19670:20107068] 异步任务4{number = 2, name = (null)}
多线程篇[19670:20107068] 异步嵌套任务1{number = 2, name = (null)}
多线程篇[19670:20107068] 异步嵌套嵌套任务1{number = 2, name = (null)}


//复杂版本
//串行队列最多只能开启两条线程,子线程执行顺序一定,先外层再内层
- (void)demo09 {
    dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.serial", NULL);
    
    dispatch_async(queue, ^{
        NSLog(@"异步任务1%@", [NSThread currentThread]);
        dispatch_async(queue, ^{
            NSLog(@"异步嵌套任务1%@", [NSThread currentThread]);
            dispatch_async(queue, ^{
                NSLog(@"异步嵌套嵌套任务1%@", [NSThread currentThread]);
            });
        });
    });
    dispatch_async(queue, ^{
        //[NSThread sleepForTimeInterval:2];
        NSLog(@"异步任务2%@", [NSThread currentThread]);
        dispatch_async(queue, ^{
            NSLog(@"异步嵌套任务2%@", [NSThread currentThread]);
            dispatch_async(queue, ^{
                NSLog(@"异步嵌套嵌套任务2%@", [NSThread currentThread]);
            });
        });
    });
    dispatch_async(queue, ^{
        NSLog(@"异步任务3%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"异步任务4%@", [NSThread currentThread]);
    });
    [NSThread sleepForTimeInterval:2];
    NSLog(@"当前任务%@", [NSThread currentThread]);
}
//执行结果:
多线程篇[20921:20153078] 异步任务1{number = 2, name = (null)}
多线程篇[20921:20153078] 异步任务2{number = 2, name = (null)}
多线程篇[20921:20153078] 异步任务3{number = 2, name = (null)}
多线程篇[20921:20153078] 异步任务4{number = 2, name = (null)}
多线程篇[20921:20153078] 异步嵌套任务1{number = 2, name = (null)}
多线程篇[20921:20153078] 异步嵌套任务2{number = 2, name = (null)}
多线程篇[20921:20153078] 异步嵌套嵌套任务1{number = 2, name = (null)}
多线程篇[20921:20153078] 异步嵌套嵌套任务2{number = 2, name = (null)}
多线程篇[20921:20153033] 当前任务{number = 1, name = main}

并行队列:dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);

  • 具体开启多少线程系统决定。用户不参与管理(包括线程的创建与销毁)
同步任务执行sync 异步任务执行async
Serial串行队列 当前线程,顺序执行 子线程,顺序执行
Concurrent并行队列 当前线程,顺序执行 很多线程,并行执行

以上可知

  1. 并行队列一定不会被阻塞
  2. 串行队列嵌套同步任务一定会阻塞
  3. ...自由发挥

你可能感兴趣的:(gcd多线程任务与队列组合分析)