美文网首页
多线程(一)

多线程(一)

作者: 紫荆秋雪_文 | 来源:发表于2018-07-19 02:34 被阅读1次

    一、常见的多线程

    1、pthread

    • pthread是一套通用的多线程API,适用于Unix\Linux\Windows等系统
    • 跨平台,是使用C语言编写的API所以使用难度大
    • 线程的生命周期需要程序员管理

    2、NSThread

    • 是使用OC语言编写,使用起来更加面向对象,简单易用,可直接操作线程对象
    • 线程的生命周期需要程序员管理

    3、GCD

    • 是使用C语言编写,为了替代NSTread等线程计算而生,充分利用了设备的多核
    • 线程的生命周期自动管理

    4、NSOperation

    • 是对GCD的封装,比GCD多了一些更加简单实用的功能,使用更加面向对象
    • 线程的生命周期自动管理 常见多线程.png

    二、GCD(Grand Central Dispatch)

    1、GCD中用来执行任务的函数

    • 同步的方式执行任务
    /**
         同步执行任务
    
         @param queue 队列
         @param block 任务
    */
    dispatch_sync(dispatch_queue_t  _Nonnull queue, block)
    
    • 用异步的方式执行任务
    /**
         异步执行任务
    
         @param queue 队列
         @param block 任务
    */
    dispatch_async(dispatch_queue_t  _Nonnull queue, block)
    

    2、GCD的队列
    1、并发队列(Concurrent Dispatch Queue)

    • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    • 并发功能只有在异步(dispatch_async)函数下才有效
      2、串行队列(Serial Dispatch Queue)
    • 让任务一个接着一个地执行(一个任务执行完毕后,在执行下一个任务)

    三、GCD实战

    1、同步主队列

    • 测试代码
    /**
     同步 主队列
     */
    - (void)gcd_syncMain_Test {
        //0.当前线程和当前队列
        dispatch_queue_t currentQ = dispatch_get_current_queue();
        //1.创建主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        NSLog(@"任务1:%@--当前队列:%p\n", [NSThread currentThread], currentQ);
        //2.在同步串行队列中执行任务
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务2:中第%d任务---当前线程:%@--当前队列:%p", i, [NSThread currentThread], queue);
            }
            
        });
        NSLog(@"\n");
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3:%@--当前队列:%p", [NSThread currentThread], currentQ);
        }
    }
    
    • 打印输出
    2018-07-19 00:35:47.881613+0800 01-GCD[5387:594727] 任务1:<NSThread: 0x604000067100>{number = 1, name = main}--当前队列:0x11469ae80
    崩溃
    
    • 崩溃信息 同步主队列.png 同步主队列.png 同步主队列二.png
    • 分析:通过代码和打印信息可以知道,代码是在主线程上执行的,所以会把任务1dispatch_sync任务3加入到主队列中等待主线程执行,但是由于dispatch_sync是一个同步主线程,由于同步主线程的特性,所以任务2会被加入到主队列中在任务1后面,在任务3前面;由于是同步函数,所以dispatch_sync会立即执行线程中的任务2,但是由于是同步函数,所以dispatch_sync任务会等待前面的gcd_syncMain_Test任务完成(也就是等待任务3完成),但是任务3等待任务2完成后才可以被执行,这样就造成了相互等待,产生死锁。

    2、同步串行队列

    • 测试代码
    /**
     同步串行队列
     */
    - (void)gcd_syncSerial_Test {
        //0.当前线程和当前队列
        dispatch_queue_t currentQ = dispatch_get_current_queue();
        //1.创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("queueSerial", DISPATCH_QUEUE_SERIAL);
        NSLog(@"任务1:%@--当前队列:%p\n", [NSThread currentThread], currentQ);
        //2.在同步串行队列中执行任务
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务2:中第%d任务---当前线程:%@--当前队列:%p", i, [NSThread currentThread], queue);
            }
            
        });
        NSLog(@"\n");
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3:%@--当前队列:%p", [NSThread currentThread], currentQ);
        }
    }
    
    • 打印输出
    2018-07-19 01:13:05.972097+0800 01-GCD[5559:620371] 任务1:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x11018ce80
    
    2018-07-19 01:13:05.972518+0800 01-GCD[5559:620371] 任务2:中第0任务---当前线程:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x604000143910
    2018-07-19 01:13:05.973389+0800 01-GCD[5559:620371] 任务2:中第1任务---当前线程:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x604000143910
    2018-07-19 01:13:05.973741+0800 01-GCD[5559:620371] 任务2:中第2任务---当前线程:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x604000143910
    2018-07-19 01:13:05.974822+0800 01-GCD[5559:620371] 任务2:中第3任务---当前线程:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x604000143910
    2018-07-19 01:13:05.975176+0800 01-GCD[5559:620371] 任务2:中第4任务---当前线程:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x604000143910
    2018-07-19 01:13:05.975299+0800 01-GCD[5559:620371] 
    2018-07-19 01:13:05.975428+0800 01-GCD[5559:620371] 任务3:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x11018ce80
    2018-07-19 01:13:05.975545+0800 01-GCD[5559:620371] 任务3:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x11018ce80
    2018-07-19 01:13:05.975786+0800 01-GCD[5559:620371] 任务3:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x11018ce80
    2018-07-19 01:13:05.976651+0800 01-GCD[5559:620371] 任务3:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x11018ce80
    2018-07-19 01:13:05.976986+0800 01-GCD[5559:620371] 任务3:<NSThread: 0x600000065040>{number = 1, name = main}--当前队列:0x11018ce80
    
    • 同步函数可以决定队列中的任务一个接着一个执行
    • 执行任务1、任务2、任务3都是在主线程上执行的,但是任务1、任务3是在同一个队列中执行,但是任务2在另外一个队列中执行
    • 为什么同步串行队列不会造成死锁 同步串行队列.png
    • 分析:从打印可以知道,任务1、任务3任务2是在不同的队列中,但是它们都是在主线程中执行。由于是同步执行,所以必须要先执行完任务1,执行完任务1会立即执行任务2,完成任务2再执行任务3。由于有2个队列所以不会发生死锁。

    3、同步并行队列

    • 测试代码
    /**
     同步并行队列
     */
    - (void)gcd_syncGlobal_Test {
        //0.当前线程和当前队列
        dispatch_queue_t currentQ = dispatch_get_current_queue();
        //1.创建并行队列(全局队列也是一种并行队列)
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        NSLog(@"任务1:%@--当前队列:%p\n", [NSThread currentThread], currentQ);
        //2.在同步并行队列中执行任务
        dispatch_sync(queue, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务2:中第%d任务---当前线程:%@--当前队列:%p", i, [NSThread currentThread], queue);
            }
            
        });
        NSLog(@"\n");
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3:%@--当前队列:%p", [NSThread currentThread], currentQ);
        }
    }
    
    • 打印输出
    2018-07-19 01:29:17.816357+0800 01-GCD[5612:630100] 任务1:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f470e80
    
    2018-07-19 01:29:17.816665+0800 01-GCD[5612:630100] 任务2:中第0任务---当前线程:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f472500
    2018-07-19 01:29:17.816829+0800 01-GCD[5612:630100] 任务2:中第1任务---当前线程:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f472500
    2018-07-19 01:29:17.816985+0800 01-GCD[5612:630100] 任务2:中第2任务---当前线程:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f472500
    2018-07-19 01:29:17.817825+0800 01-GCD[5612:630100] 任务2:中第3任务---当前线程:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f472500
    2018-07-19 01:29:17.819165+0800 01-GCD[5612:630100] 任务2:中第4任务---当前线程:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f472500
    2018-07-19 01:29:17.820390+0800 01-GCD[5612:630100] 
    2018-07-19 01:29:17.820808+0800 01-GCD[5612:630100] 任务3:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f470e80
    2018-07-19 01:29:17.820923+0800 01-GCD[5612:630100] 任务3:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f470e80
    2018-07-19 01:29:17.821028+0800 01-GCD[5612:630100] 任务3:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f470e80
    2018-07-19 01:29:17.821291+0800 01-GCD[5612:630100] 任务3:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f470e80
    2018-07-19 01:29:17.821423+0800 01-GCD[5612:630100] 任务3:<NSThread: 0x60400006c980>{number = 1, name = main}--当前队列:0x10f470e80
    
    • 同步并行队列和同步串行队列情况一样
    • 同步函数小结
      • 在当前线程执行任务
      • 任务是一个接着一个执行

    4、异步串行队列

    • 测试代码
    /**
     异步串行队列
     */
    - (void)gcd_asyncSerial_Test {
        //0.当前线程和当前队列
        dispatch_queue_t currentQ = dispatch_get_current_queue();
        //1.手动创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("asyncSerial", DISPATCH_QUEUE_SERIAL);
        NSLog(@"任务1:%@--当前队列:%p\n", [NSThread currentThread], currentQ);
        //2.在同步并行队列中执行任务
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务2:中第%d任务---当前线程:%@--当前队列:%p", i, [NSThread currentThread], queue);
            }
            
        });
        NSLog(@"\n");
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3:%@--当前队列:%p", [NSThread currentThread], currentQ);
        }
    }
    
    • 打印输出
    2018-07-19 01:38:53.699845+0800 01-GCD[5663:636377] 任务1:<NSThread: 0x60000006bac0>{number = 1, name = main}--当前队列:0x10c0d7e80
    
    2018-07-19 01:38:53.700075+0800 01-GCD[5663:636377] 
    2018-07-19 01:38:53.700202+0800 01-GCD[5663:636451] 任务2:中第0任务---当前线程:<NSThread: 0x604000465b80>{number = 4, name = (null)}--当前队列:0x6040001569a0
    2018-07-19 01:38:53.700432+0800 01-GCD[5663:636451] 任务2:中第1任务---当前线程:<NSThread: 0x604000465b80>{number = 4, name = (null)}--当前队列:0x6040001569a0
    2018-07-19 01:38:53.700435+0800 01-GCD[5663:636377] 任务3:<NSThread: 0x60000006bac0>{number = 1, name = main}--当前队列:0x10c0d7e80
    2018-07-19 01:38:53.700589+0800 01-GCD[5663:636451] 任务2:中第2任务---当前线程:<NSThread: 0x604000465b80>{number = 4, name = (null)}--当前队列:0x6040001569a0
    2018-07-19 01:38:53.700676+0800 01-GCD[5663:636377] 任务3:<NSThread: 0x60000006bac0>{number = 1, name = main}--当前队列:0x10c0d7e80
    2018-07-19 01:38:53.700719+0800 01-GCD[5663:636451] 任务2:中第3任务---当前线程:<NSThread: 0x604000465b80>{number = 4, name = (null)}--当前队列:0x6040001569a0
    2018-07-19 01:38:53.700792+0800 01-GCD[5663:636377] 任务3:<NSThread: 0x60000006bac0>{number = 1, name = main}--当前队列:0x10c0d7e80
    2018-07-19 01:38:53.700846+0800 01-GCD[5663:636451] 任务2:中第4任务---当前线程:<NSThread: 0x604000465b80>{number = 4, name = (null)}--当前队列:0x6040001569a0
    2018-07-19 01:38:53.701907+0800 01-GCD[5663:636377] 任务3:<NSThread: 0x60000006bac0>{number = 1, name = main}--当前队列:0x10c0d7e80
    2018-07-19 01:38:53.703963+0800 01-GCD[5663:636377] 任务3:<NSThread: 0x60000006bac0>{number = 1, name = main}--当前队列:0x10c0d7e80
    
    • 获取主队列的异步串行队列测试
    /**
     异步串行队列
     */
    - (void)gcd_asyncSerial_Test {
        //0.当前线程和当前队列
        dispatch_queue_t currentQ = dispatch_get_current_queue();
        //1.获取主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        NSLog(@"任务1:%@--当前队列:%p\n", [NSThread currentThread], currentQ);
        //2.在同步并行队列中执行任务
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务2:中第%d任务---当前线程:%@--当前队列:%p", i, [NSThread currentThread], queue);
            }
            
        });
        NSLog(@"\n");
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3:%@--当前队列:%p", [NSThread currentThread], currentQ);
        }
    }
    
    • 打印输出
    2018-07-19 01:46:21.216192+0800 01-GCD[5700:641200] 任务1:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    
    2018-07-19 01:46:21.216456+0800 01-GCD[5700:641200] 
    2018-07-19 01:46:21.216593+0800 01-GCD[5700:641200] 任务3:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.216711+0800 01-GCD[5700:641200] 任务3:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.216859+0800 01-GCD[5700:641200] 任务3:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.217054+0800 01-GCD[5700:641200] 任务3:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.217519+0800 01-GCD[5700:641200] 任务3:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.218070+0800 01-GCD[5700:641200] 任务2:中第0任务---当前线程:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.218608+0800 01-GCD[5700:641200] 任务2:中第1任务---当前线程:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.219841+0800 01-GCD[5700:641200] 任务2:中第2任务---当前线程:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.220003+0800 01-GCD[5700:641200] 任务2:中第3任务---当前线程:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    2018-07-19 01:46:21.220561+0800 01-GCD[5700:641200] 任务2:中第4任务---当前线程:<NSThread: 0x600000067b80>{number = 1, name = main}--当前队列:0x111b23e80
    
    • dispatch_async有开线程的能力,但是如果是在主队列时,不开线程
    • dispatch_async有开线程的能力,但是如果是在非主队列时,开线程 异步串行队列.png

    5、异步并行队列

    • 测试代码
    /**
     异步并行队列
     */
    - (void)gcd_asyncConcurrent_Test {
        //0.当前线程和当前队列
        dispatch_queue_t currentQ = dispatch_get_current_queue();
        //1.创建并行队列
        dispatch_queue_t queue = dispatch_queue_create("asyncCONCURRENT", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"任务1:%@--当前队列:%p\n", [NSThread currentThread], currentQ);
        //2.在同步并行队列中执行任务
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务2:中第%d任务---当前线程:%@--当前队列:%p", i, [NSThread currentThread], queue);
            }
            
        });
        NSLog(@"\n");
        for (int i = 0; i < 5; i++) {
            NSLog(@"任务3:%@--当前队列:%p", [NSThread currentThread], currentQ);
        }
    }
    
    • 打印输出
    2018-07-19 01:53:55.849240+0800 01-GCD[5735:646211] 任务1:<NSThread: 0x60400007ee40>{number = 1, name = main}--当前队列:0x109aa8e80
    
    2018-07-19 01:53:55.849447+0800 01-GCD[5735:646211] 
    2018-07-19 01:53:55.849488+0800 01-GCD[5735:646255] 任务2:中第0任务---当前线程:<NSThread: 0x604000468e80>{number = 3, name = (null)}--当前队列:0x604000153820
    2018-07-19 01:53:55.849680+0800 01-GCD[5735:646211] 任务3:<NSThread: 0x60400007ee40>{number = 1, name = main}--当前队列:0x109aa8e80
    2018-07-19 01:53:55.849783+0800 01-GCD[5735:646255] 任务2:中第1任务---当前线程:<NSThread: 0x604000468e80>{number = 3, name = (null)}--当前队列:0x604000153820
    2018-07-19 01:53:55.850067+0800 01-GCD[5735:646211] 任务3:<NSThread: 0x60400007ee40>{number = 1, name = main}--当前队列:0x109aa8e80
    2018-07-19 01:53:55.850155+0800 01-GCD[5735:646255] 任务2:中第2任务---当前线程:<NSThread: 0x604000468e80>{number = 3, name = (null)}--当前队列:0x604000153820
    2018-07-19 01:53:55.850663+0800 01-GCD[5735:646211] 任务3:<NSThread: 0x60400007ee40>{number = 1, name = main}--当前队列:0x109aa8e80
    2018-07-19 01:53:55.851082+0800 01-GCD[5735:646255] 任务2:中第3任务---当前线程:<NSThread: 0x604000468e80>{number = 3, name = (null)}--当前队列:0x604000153820
    2018-07-19 01:53:55.851373+0800 01-GCD[5735:646211] 任务3:<NSThread: 0x60400007ee40>{number = 1, name = main}--当前队列:0x109aa8e80
    2018-07-19 01:53:55.852096+0800 01-GCD[5735:646255] 任务2:中第4任务---当前线程:<NSThread: 0x604000468e80>{number = 3, name = (null)}--当前队列:0x604000153820
    2018-07-19 01:53:55.852750+0800 01-GCD[5735:646211] 任务3:<NSThread: 0x60400007ee40>{number = 1, name = main}--当前队列:0x109aa8e80
    
    • dispatch_async有开线程的能力 异步并行队列.png

    6、子线程死锁

    • 测试代码
    /**
     子线程串行队列死锁
     */
    - (void)gcd_asyncLock_Test {
        //0.当前线程和当前队列
        dispatch_queue_t currentQ = dispatch_get_current_queue();
        //1.创建并行队列
        dispatch_queue_t queue = dispatch_queue_create("asyncLock", DISPATCH_QUEUE_SERIAL);
        NSLog(@"任务1");
        //2.在同步并行队列中执行任务
        dispatch_async(queue, ^{
            NSLog(@"任务2");
            dispatch_sync(queue, ^{
                NSLog(@"任务3");
            });
            
        });
        NSLog(@"任务4");
    }
    
    • 打印输出
    2018-07-19 02:13:25.627098+0800 01-GCD[5930:662760] 任务1
    2018-07-19 02:13:25.627295+0800 01-GCD[5930:662760] 任务4
    2018-07-19 02:13:25.627313+0800 01-GCD[5930:662829] 任务2
    (lldb) 
    
    • 子线程死锁分析 子线程串行死锁.png

    四、小结

    • 1、同步和异步主要影响:能不能开启新的线程

      同步:在当前线程中执行任务,不具备开启新线程的能力
      异步:在新的线程中执行任务,具备开启新线程的能力

    • 2、并发和串行主要影响:任务的执行方式

      并发多个任务并发(同时)执行
      串行一个任务执行完毕后,再执行下一个任务

    • 3、使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)
      总结.png

    五、队列组

    • 队列组可以实现多个任务的执行顺序可控
    • 异步并发执行任务1、任务2,等任务1、任务2都执行完毕后,再回到主线程执行任务3
    • 测试代码
    /**
     异步并发执行任务1、任务2,
     等任务1、任务2都执行完毕后,再回到主线程执行任务3
     */
    - (void)taskFun {
        //1.创建队列组
        dispatch_group_t group = dispatch_group_create();
        //2.创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        //3.在队列组中异步执行任务
        dispatch_group_async(group, queue, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务1---%@", [NSThread currentThread]);
            }
        });
        
        dispatch_group_async(group, queue, ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务2---%@", [NSThread currentThread]);
            }
        });
        
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            for (int i = 0; i < 5; i++) {
                NSLog(@"任务3---%@", [NSThread currentThread]);
            }
        });
        
    }
    
    • 打印输出
    2018-07-19 22:30:25.586603+0800 02-队列组[1481:70343] 任务1---<NSThread: 0x60400007ebc0>{number = 3, name = (null)}
    2018-07-19 22:30:25.586603+0800 02-队列组[1481:70416] 任务2---<NSThread: 0x604000264640>{number = 4, name = (null)}
    2018-07-19 22:30:25.586828+0800 02-队列组[1481:70343] 任务1---<NSThread: 0x60400007ebc0>{number = 3, name = (null)}
    2018-07-19 22:30:25.586848+0800 02-队列组[1481:70416] 任务2---<NSThread: 0x604000264640>{number = 4, name = (null)}
    2018-07-19 22:30:25.587274+0800 02-队列组[1481:70343] 任务1---<NSThread: 0x60400007ebc0>{number = 3, name = (null)}
    2018-07-19 22:30:25.587307+0800 02-队列组[1481:70416] 任务2---<NSThread: 0x604000264640>{number = 4, name = (null)}
    2018-07-19 22:30:25.587445+0800 02-队列组[1481:70343] 任务1---<NSThread: 0x60400007ebc0>{number = 3, name = (null)}
    2018-07-19 22:30:25.588589+0800 02-队列组[1481:70416] 任务2---<NSThread: 0x604000264640>{number = 4, name = (null)}
    2018-07-19 22:30:25.590428+0800 02-队列组[1481:70343] 任务1---<NSThread: 0x60400007ebc0>{number = 3, name = (null)}
    2018-07-19 22:30:25.591197+0800 02-队列组[1481:70416] 任务2---<NSThread: 0x604000264640>{number = 4, name = (null)}
    2018-07-19 22:30:25.591991+0800 02-队列组[1481:70295] 任务3---<NSThread: 0x604000065e00>{number = 1, name = main}
    2018-07-19 22:30:25.592272+0800 02-队列组[1481:70295] 任务3---<NSThread: 0x604000065e00>{number = 1, name = main}
    2018-07-19 22:30:25.592861+0800 02-队列组[1481:70295] 任务3---<NSThread: 0x604000065e00>{number = 1, name = main}
    2018-07-19 22:30:25.593492+0800 02-队列组[1481:70295] 任务3---<NSThread: 0x604000065e00>{number = 1, name = main}
    2018-07-19 22:30:25.595441+0800 02-队列组[1481:70295] 任务3---<NSThread: 0x604000065e00>{number = 1, name = main}
    

    六、多线程和RunLoop

    实践1

    • 测试代码
    - (void)gcdRunloopTest {
        //1.创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        //2.异步执行任务
        dispatch_async(queue, ^{
            NSLog(@"任务1");
            [self performSelector:@selector(taskTest) withObject:nil afterDelay:.0];
            NSLog(@"任务3");
        });
    }
    
    - (void)taskTest {
        NSLog(@"任务2");
    }
    
    • 打印输出
    2018-07-19 22:37:14.672885+0800 02-队列组[1563:75572] 任务1
    2018-07-19 22:37:14.673153+0800 02-队列组[1563:75572] 任务3
    
    • 为什么没有执行任务2

    下面更换一下performSelector:withObject:方法

    - (void)gcdRunloopTest {
        //1.创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        //2.异步执行任务
        dispatch_async(queue, ^{
            NSLog(@"任务1");
    //        [self performSelector:@selector(taskTest) withObject:nil afterDelay:.0];
            [self performSelector:@selector(taskTest) withObject:nil];
            NSLog(@"任务3");
        });
    }
    
    - (void)taskTest {
        NSLog(@"任务2");
    }
    
    • 打印输出
    2018-07-19 22:39:34.639733+0800 02-队列组[1594:77477] 任务1
    2018-07-19 22:39:34.639951+0800 02-队列组[1594:77477] 任务2
    2018-07-19 22:39:34.640234+0800 02-队列组[1594:77477] 任务3
    
    • 分析:performSelector:withObject:afterDelay:方法之所以不会执行是因为这个方法是需要依赖RunLoop来执行的,但是由于此时的线程是子线程默认是不会开启RunLoop的,所以才不会执行这个方法
    performSelector:withObject:afterDelay:方法是NSRunLoop.h文件中的方法
    performSelector:withObject方法是NSObject.h文件中的方法,所以这个方法底层会转成
    objc_msgSend(self, @selector(taskTest))消息来发送;
    
    • 解决方案
      • 在当前子线程中开启RunLoop
    - (void)gcdRunloopTest {
        //1.创建并发队列
        dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
        //2.异步执行任务
        dispatch_async(queue, ^{
            NSLog(@"任务1");
            [self performSelector:@selector(taskTest) withObject:nil afterDelay:.0];
            NSLog(@"任务3");
            //必须给RunLoop中添加port、Observer、Timer后才能保住线程
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            //开启线程
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        });
    }
    
    - (void)taskTest {
        NSLog(@"任务2");
    }
    打印输出:
    2018-07-19 22:54:27.041532+0800 02-队列组[1787:88148] 任务1
    2018-07-19 22:54:27.042103+0800 02-队列组[1787:88148] 任务3
    2018-07-19 22:54:27.042587+0800 02-队列组[1787:88148] 任务2
    

    实例2

    • 测试代码
    - (void)gcdRunloopTest {
        
        NSThread *thread = [[NSThread alloc] initWithBlock:^{
            NSLog(@"任务1");
        }];
        [thread start];
        [self performSelector:@selector(taskTest) onThread:thread withObject:nil waitUntilDone:YES];
        NSLog(@"任务3");
    }
    
    - (void)taskTest {
        NSLog(@"任务2");
    }
    
    • 打印输出
    
    2018-07-19 23:00:02.872430+0800 02-队列组[1857:92702] 任务1
    2018-07-19 23:00:02.888592+0800 02-队列组[1857:92626] *** Terminating app due to uncaught exception 'NSDestinationInvalidException', reason: '*** -[ViewController performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform'
    
    • 分析:当执行performSelector:onThread:withObject:waitUntilDone:方法的时候thread线程已经执行完任务自动释放掉了
    • 解决方案
    - (void)gcdRunloopTest {
        
        NSThread *thread = [[NSThread alloc] initWithBlock:^{
            NSLog(@"任务1");
            //必须给RunLoop中添加port、Observer、Timer后才能保住线程
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
            //开启线程
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }];
        [thread start];
        [self performSelector:@selector(taskTest) onThread:thread withObject:nil waitUntilDone:YES];
        NSLog(@"任务3");
    }
    
    - (void)taskTest {
        NSLog(@"任务2");
    }
    
    • 打印输出
    2018-07-19 23:04:16.362816+0800 02-队列组[1930:96441] 任务1
    2018-07-19 23:04:16.363482+0800 02-队列组[1930:96441] 任务2
    2018-07-19 23:04:16.363770+0800 02-队列组[1930:96357] 任务3
    

    相关文章

      网友评论

          本文标题:多线程(一)

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