美文网首页
gcd多线程任务与队列组合分析

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

作者: 风紧扯呼丶 | 来源:发表于2019-11-29 10:42 被阅读0次

    关于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 <NSThread: 0x600001776c00>{number = 2, name = (null)}
    多线程篇[13337:19767072] 嵌套同步任务 <NSThread: 0x600001776c00>{number = 2, name = (null)}
    多线程篇[13337:19767072] 嵌套异步任务222 <NSThread: 0x600001776c00>{number = 2, name = (null)}
    多线程篇[13337:19767075] 嵌套异步任务111 <NSThread: 0x60000179e500>{number = 3, name = (null)}
    多线程篇[13337:19767085] 嵌套异步任务333 <NSThread: 0x60000179e7c0>{number = 5, name = (null)}
    多线程篇[13337:19767087] 嵌套异步任务444 <NSThread: 0x6000017a4940>{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] 同步任务<NSThread: 0x600001703e80>{number = 1, name = main}
    多线程篇[15215:19872610] 当前任务<NSThread: 0x600001703e80>{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] 开始 - <NSThread: 0x600001706bc0>{number = 1, name = main}
    多线程篇[17909:20024157] 结束 - <NSThread: 0x600001706bc0>{number = 1, name = main}
    多线程篇[17909:20024192] 异步任务前段 - <NSThread: 0x600001744000>{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] 开始 - <NSThread: 0x600001703e80>{number = 1, name = main}
    多线程篇[18897:20066510] 同步任务11前段 - <NSThread: 0x600001703e80>{number = 1, name = main}
    多线程篇[18897:20066510] 同步任务22 - <NSThread: 0x600001703e80>{number = 1, name = main}
    多线程篇[18897:20066510] 同步任务11后段 - <NSThread: 0x600001703e80>{number = 1, name = main}
    多线程篇[18897:20066510] 结束 - <NSThread: 0x600001703e80>{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] 开始 - <NSThread: 0x60000170abc0>{number = 1, name = main}
    多线程篇[18384:20049195] 结束 - <NSThread: 0x60000170abc0>{number = 1, name = main}
    多线程篇[18384:20049509] 异步任务前段 - <NSThread: 0x600001778500>{number = 2, name = (null)}
    多线程篇[18384:20049509] 同步任务 - <NSThread: 0x600001778500>{number = 2, name = (null)}
    多线程篇[18384:20049509] 异步任务后段 - <NSThread: 0x600001778500>{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] 当前任务<NSThread: 0x600001711840>{number = 1, name = main}
    多线程篇[19670:20107068] 异步任务1<NSThread: 0x600001778000>{number = 2, name = (null)}
    多线程篇[19670:20107068] 异步任务2<NSThread: 0x600001778000>{number = 2, name = (null)}
    多线程篇[19670:20107068] 异步任务3<NSThread: 0x600001778000>{number = 2, name = (null)}
    多线程篇[19670:20107068] 异步任务4<NSThread: 0x600001778000>{number = 2, name = (null)}
    多线程篇[19670:20107068] 异步嵌套任务1<NSThread: 0x600001778000>{number = 2, name = (null)}
    多线程篇[19670:20107068] 异步嵌套嵌套任务1<NSThread: 0x600001778000>{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<NSThread: 0x600001780d40>{number = 2, name = (null)}
    多线程篇[20921:20153078] 异步任务2<NSThread: 0x600001780d40>{number = 2, name = (null)}
    多线程篇[20921:20153078] 异步任务3<NSThread: 0x600001780d40>{number = 2, name = (null)}
    多线程篇[20921:20153078] 异步任务4<NSThread: 0x600001780d40>{number = 2, name = (null)}
    多线程篇[20921:20153078] 异步嵌套任务1<NSThread: 0x600001780d40>{number = 2, name = (null)}
    多线程篇[20921:20153078] 异步嵌套任务2<NSThread: 0x600001780d40>{number = 2, name = (null)}
    多线程篇[20921:20153078] 异步嵌套嵌套任务1<NSThread: 0x600001780d40>{number = 2, name = (null)}
    多线程篇[20921:20153078] 异步嵌套嵌套任务2<NSThread: 0x600001780d40>{number = 2, name = (null)}
    多线程篇[20921:20153033] 当前任务<NSThread: 0x600001705880>{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多线程任务与队列组合分析

          本文链接:https://www.haomeiwen.com/subject/zhmwwctx.html