任务与队列 iOS之多线程GCD(一)

1,763 阅读8分钟

队列

特点:先进先出的数据结构

分类:串行队列 并行队列 全局并行队列 主队列

任务:就是block代码快要执行的代码 同步执行(sync) 异步执行(async)

多线程 简单理解就是除主线程外开辟了其他线程、增加执行效率。大白话就是代码有多条执行路径。对于单核的iOS系统、多线程之间其实是并发的而不是多核的并行。也就是单核的iOS系统,各个线程在单位时间是来回切换的、造成了并行的假象。

GCD是基于c语言封装的、在这里只谈谈用法。用法就是创建队列,将任务添加到队列中执行。

如何创建队列?

串行队列
dispatch_queue_t queue1 = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);
主队列 特殊的串行队列---所有主队列的任务都会放在主线程执行
dispatch_queue_t mainQueue = dispatch_get_main_queue();
并行队列
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
全局并行队列
dispatch_queue_t queueAll = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

任务

任务分为同步执行、异步执行

同步不具备开启线程的能力
dispatch_sync(queue, ^{
    <#code#>
});
异步具备开启线程的能力、具体是否开启了线程要结合队列
dispatch_async(queue1, ^{
    <#code#>
});

任务和队列组合 page表格截图如下:(由于全局并发队列也可以看作并发队列、所以不予考虑)

一、同步+串行队列

特点:不开启新的线程、任务按顺序执行

- (void)syncSerial {
    NSLog(@"当前线程为:%@",[NSThread currentThread]);  // 当前线程
    NSLog(@"开始");
    //这里创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务一、来自线程:%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务二、来自线程:%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务三、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"结束");
}

打印结果:

2019-01-23 09:49:41.098930+0800 多线程demo[922:34551] 当前线程为:<NSThread: 0x604000073a80>{number = 1, name = main}
2019-01-23 09:49:41.099092+0800 多线程demo[922:34551] 开始
2019-01-23 09:49:43.100540+0800 多线程demo[922:34551] 我是任务一、来自线程:<NSThread: 0x604000073a80>{number = 1, name = main}
2019-01-23 09:49:45.101953+0800 多线程demo[922:34551] 我是任务一、来自线程:<NSThread: 0x604000073a80>{number = 1, name = main}
2019-01-23 09:49:47.103420+0800 多线程demo[922:34551] 我是任务二、来自线程:<NSThread: 0x604000073a80>{number = 1, name = main}
2019-01-23 09:49:49.104828+0800 多线程demo[922:34551] 我是任务二、来自线程:<NSThread: 0x604000073a80>{number = 1, name = main}
2019-01-23 09:49:51.106334+0800 多线程demo[922:34551] 我是任务三、来自线程:<NSThread: 0x604000073a80>{number = 1, name = main}
2019-01-23 09:49:53.106785+0800 多线程demo[922:34551] 我是任务三、来自线程:<NSThread: 0x604000073a80>{number = 1, name = main}
2019-01-23 09:49:53.106981+0800 多线程demo[922:34551] 结束

二、同步+并发队列

特点:不开启新的线程、任务按顺序执行

NSLog(@"当前线程%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"开始");
    
    dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务一、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务二、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务三、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"结束");

打印结果:

2019-01-23 10:02:31.505366+0800 多线程demo[1053:44225] 当前线程<NSThread: 0x600000064d00>{number = 1, name = main}
2019-01-23 10:02:31.505740+0800 多线程demo[1053:44225] 开始
2019-01-23 10:02:33.506119+0800 多线程demo[1053:44225] 我是任务一、来自线程:<NSThread: 0x600000064d00>{number = 1, name = main}
2019-01-23 10:02:35.507093+0800 多线程demo[1053:44225] 我是任务一、来自线程:<NSThread: 0x600000064d00>{number = 1, name = main}
2019-01-23 10:02:37.507964+0800 多线程demo[1053:44225] 我是任务二、来自线程:<NSThread: 0x600000064d00>{number = 1, name = main}
2019-01-23 10:02:39.508543+0800 多线程demo[1053:44225] 我是任务二、来自线程:<NSThread: 0x600000064d00>{number = 1, name = main}
2019-01-23 10:02:41.509973+0800 多线程demo[1053:44225] 我是任务三、来自线程:<NSThread: 0x600000064d00>{number = 1, name = main}
2019-01-23 10:02:43.511403+0800 多线程demo[1053:44225] 我是任务三、来自线程:<NSThread: 0x600000064d00>{number = 1, name = main}
2019-01-23 10:02:43.511608+0800 多线程demo[1053:44225] 结束

三、异步+串行队列

特点:开启了新的线程、任务顺序执行

  NSLog(@"当前线程%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"开始");
    dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务一、来自线程:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务二、来自线程:%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务三、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"结束");

打印结果:

2019-01-23 10:09:26.140699+0800 多线程demo[1116:48819] 当前线程<NSThread: 0x600000076040>{number = 1, name = main}
2019-01-23 10:09:26.140906+0800 多线程demo[1116:48819] 开始
2019-01-23 10:09:26.141049+0800 多线程demo[1116:48819] 结束
2019-01-23 10:09:28.143936+0800 多线程demo[1116:48854] 我是任务一、来自线程:<NSThread: 0x6000004629c0>{number = 3, name = (null)}
2019-01-23 10:09:30.148732+0800 多线程demo[1116:48854] 我是任务一、来自线程:<NSThread: 0x6000004629c0>{number = 3, name = (null)}
2019-01-23 10:09:32.154261+0800 多线程demo[1116:48854] 我是任务二、来自线程:<NSThread: 0x6000004629c0>{number = 3, name = (null)}
2019-01-23 10:09:34.156924+0800 多线程demo[1116:48854] 我是任务二、来自线程:<NSThread: 0x6000004629c0>{number = 3, name = (null)}
2019-01-23 10:09:36.158106+0800 多线程demo[1116:48854] 我是任务三、来自线程:<NSThread: 0x6000004629c0>{number = 3, name = (null)}
2019-01-23 10:09:38.162608+0800 多线程demo[1116:48854] 我是任务三、来自线程:<NSThread: 0x6000004629c0>{number = 3, name = (null)}

四、异步+并发队列

特点:开启新的线程、任务交替执行

    NSLog(@"当前线程%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"开始");
    
    dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务一、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];            
            NSLog(@"我是任务二、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务三、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"结束");

打印结果:

2019-01-23 10:15:29.424356+0800 多线程demo[1190:53683] 当前线程<NSThread: 0x604000074740>{number = 1, name = main}
2019-01-23 10:15:29.424575+0800 多线程demo[1190:53683] 开始
2019-01-23 10:15:29.424723+0800 多线程demo[1190:53683] 结束
2019-01-23 10:15:31.430195+0800 多线程demo[1190:53767] 我是任务三、来自线程:<NSThread: 0x60400047f5c0>{number = 5, name = (null)}
2019-01-23 10:15:31.430195+0800 多线程demo[1190:53764] 我是任务二、来自线程:<NSThread: 0x600000278a80>{number = 3, name = (null)}
2019-01-23 10:15:31.430195+0800 多线程demo[1190:53763] 我是任务一、来自线程:<NSThread: 0x60400047fe40>{number = 4, name = (null)}
2019-01-23 10:15:33.435111+0800 多线程demo[1190:53763] 我是任务一、来自线程:<NSThread: 0x60400047fe40>{number = 4, name = (null)}
2019-01-23 10:15:33.435111+0800 多线程demo[1190:53767] 我是任务三、来自线程:<NSThread: 0x60400047f5c0>{number = 5, name = (null)}
2019-01-23 10:15:33.435111+0800 多线程demo[1190:53764] 我是任务二、来自线程:<NSThread: 0x600000278a80>{number = 3, name = (null)}

五、异步+主队列(任务执行 类似为同步+串行队列)

特点:主线程执行(主队列的任务都是在主线程)、任务按顺序执行

    NSLog(@"当前线程为:%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"开始");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务一、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"我是任务二、来自线程:%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"我是任务三、来自线程:%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    NSLog(@"结束");

打印结果:

2019-01-23 10:19:33.503411+0800 多线程demo[1242:56892] 当前线程为:<NSThread: 0x60000006eac0>{number = 1, name = main}
2019-01-23 10:19:33.503588+0800 多线程demo[1242:56892] 开始
2019-01-23 10:19:33.503736+0800 多线程demo[1242:56892] 结束
2019-01-23 10:19:35.538348+0800 多线程demo[1242:56892] 我是任务一、来自线程:<NSThread: 0x60000006eac0>{number = 1, name = main}
2019-01-23 10:19:37.539726+0800 多线程demo[1242:56892] 我是任务一、来自线程:<NSThread: 0x60000006eac0>{number = 1, name = main}
2019-01-23 10:19:39.541125+0800 多线程demo[1242:56892] 我是任务二、来自线程:<NSThread: 0x60000006eac0>{number = 1, name = main}
2019-01-23 10:19:41.542511+0800 多线程demo[1242:56892] 我是任务二、来自线程:<NSThread: 0x60000006eac0>{number = 1, name = main}
2019-01-23 10:19:43.543439+0800 多线程demo[1242:56892] 我是任务三、来自线程:<NSThread: 0x60000006eac0>{number = 1, name = main}
2019-01-23 10:19:45.544663+0800 多线程demo[1242:56892] 我是任务三、来自线程:<NSThread: 0x60000006eac0>{number = 1, name = main}

六、同步+主队列

特点:队列引起的死锁

- (void)syncMain {
    NSLog(@"当前线程:%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"开始");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务一、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];
            NSLog(@"我是任务二、来自线程:%@",[NSThread currentThread]);
        }
    });
    NSLog(@"结束");
}

打印结果:

2019-01-23 10:25:00.640438+0800 多线程demo[1307:61022] 当前线程:<NSThread: 0x600000069a80>{number = 1, name = main}
2019-01-23 10:25:00.640607+0800 多线程demo[1307:61022] 开始
(lldb) 崩溃

分析:由于主队列是串行队列、我们把syncMain方法看成一个任务、任务一看成一个任务。由于这两个任务都在主队列执行。主队列是特殊的串行队列。特点是任务按顺序执行。所以先执行syncMain、而syncMain要等任务一执行完才能结束。任务一要等syncMain结束再执行。两个任务互相等待。造成死锁。这就是为什么同步在主队列执行会死锁而同步在串行队列不死锁的原因。