多线程(一)

一、常见的多线程

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:{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:{number = 1, name = main}--当前队列:0x11018ce80

2018-07-19 01:13:05.972518+0800 01-GCD[5559:620371] 任务2:中第0任务---当前线程:{number = 1, name = main}--当前队列:0x604000143910
2018-07-19 01:13:05.973389+0800 01-GCD[5559:620371] 任务2:中第1任务---当前线程:{number = 1, name = main}--当前队列:0x604000143910
2018-07-19 01:13:05.973741+0800 01-GCD[5559:620371] 任务2:中第2任务---当前线程:{number = 1, name = main}--当前队列:0x604000143910
2018-07-19 01:13:05.974822+0800 01-GCD[5559:620371] 任务2:中第3任务---当前线程:{number = 1, name = main}--当前队列:0x604000143910
2018-07-19 01:13:05.975176+0800 01-GCD[5559:620371] 任务2:中第4任务---当前线程:{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:{number = 1, name = main}--当前队列:0x11018ce80
2018-07-19 01:13:05.975545+0800 01-GCD[5559:620371] 任务3:{number = 1, name = main}--当前队列:0x11018ce80
2018-07-19 01:13:05.975786+0800 01-GCD[5559:620371] 任务3:{number = 1, name = main}--当前队列:0x11018ce80
2018-07-19 01:13:05.976651+0800 01-GCD[5559:620371] 任务3:{number = 1, name = main}--当前队列:0x11018ce80
2018-07-19 01:13:05.976986+0800 01-GCD[5559:620371] 任务3:{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:{number = 1, name = main}--当前队列:0x10f470e80

2018-07-19 01:29:17.816665+0800 01-GCD[5612:630100] 任务2:中第0任务---当前线程:{number = 1, name = main}--当前队列:0x10f472500
2018-07-19 01:29:17.816829+0800 01-GCD[5612:630100] 任务2:中第1任务---当前线程:{number = 1, name = main}--当前队列:0x10f472500
2018-07-19 01:29:17.816985+0800 01-GCD[5612:630100] 任务2:中第2任务---当前线程:{number = 1, name = main}--当前队列:0x10f472500
2018-07-19 01:29:17.817825+0800 01-GCD[5612:630100] 任务2:中第3任务---当前线程:{number = 1, name = main}--当前队列:0x10f472500
2018-07-19 01:29:17.819165+0800 01-GCD[5612:630100] 任务2:中第4任务---当前线程:{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:{number = 1, name = main}--当前队列:0x10f470e80
2018-07-19 01:29:17.820923+0800 01-GCD[5612:630100] 任务3:{number = 1, name = main}--当前队列:0x10f470e80
2018-07-19 01:29:17.821028+0800 01-GCD[5612:630100] 任务3:{number = 1, name = main}--当前队列:0x10f470e80
2018-07-19 01:29:17.821291+0800 01-GCD[5612:630100] 任务3:{number = 1, name = main}--当前队列:0x10f470e80
2018-07-19 01:29:17.821423+0800 01-GCD[5612:630100] 任务3:{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:{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任务---当前线程:{number = 4, name = (null)}--当前队列:0x6040001569a0
2018-07-19 01:38:53.700432+0800 01-GCD[5663:636451] 任务2:中第1任务---当前线程:{number = 4, name = (null)}--当前队列:0x6040001569a0
2018-07-19 01:38:53.700435+0800 01-GCD[5663:636377] 任务3:{number = 1, name = main}--当前队列:0x10c0d7e80
2018-07-19 01:38:53.700589+0800 01-GCD[5663:636451] 任务2:中第2任务---当前线程:{number = 4, name = (null)}--当前队列:0x6040001569a0
2018-07-19 01:38:53.700676+0800 01-GCD[5663:636377] 任务3:{number = 1, name = main}--当前队列:0x10c0d7e80
2018-07-19 01:38:53.700719+0800 01-GCD[5663:636451] 任务2:中第3任务---当前线程:{number = 4, name = (null)}--当前队列:0x6040001569a0
2018-07-19 01:38:53.700792+0800 01-GCD[5663:636377] 任务3:{number = 1, name = main}--当前队列:0x10c0d7e80
2018-07-19 01:38:53.700846+0800 01-GCD[5663:636451] 任务2:中第4任务---当前线程:{number = 4, name = (null)}--当前队列:0x6040001569a0
2018-07-19 01:38:53.701907+0800 01-GCD[5663:636377] 任务3:{number = 1, name = main}--当前队列:0x10c0d7e80
2018-07-19 01:38:53.703963+0800 01-GCD[5663:636377] 任务3:{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:{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:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.216711+0800 01-GCD[5700:641200] 任务3:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.216859+0800 01-GCD[5700:641200] 任务3:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.217054+0800 01-GCD[5700:641200] 任务3:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.217519+0800 01-GCD[5700:641200] 任务3:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.218070+0800 01-GCD[5700:641200] 任务2:中第0任务---当前线程:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.218608+0800 01-GCD[5700:641200] 任务2:中第1任务---当前线程:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.219841+0800 01-GCD[5700:641200] 任务2:中第2任务---当前线程:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.220003+0800 01-GCD[5700:641200] 任务2:中第3任务---当前线程:{number = 1, name = main}--当前队列:0x111b23e80
2018-07-19 01:46:21.220561+0800 01-GCD[5700:641200] 任务2:中第4任务---当前线程:{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:{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任务---当前线程:{number = 3, name = (null)}--当前队列:0x604000153820
2018-07-19 01:53:55.849680+0800 01-GCD[5735:646211] 任务3:{number = 1, name = main}--当前队列:0x109aa8e80
2018-07-19 01:53:55.849783+0800 01-GCD[5735:646255] 任务2:中第1任务---当前线程:{number = 3, name = (null)}--当前队列:0x604000153820
2018-07-19 01:53:55.850067+0800 01-GCD[5735:646211] 任务3:{number = 1, name = main}--当前队列:0x109aa8e80
2018-07-19 01:53:55.850155+0800 01-GCD[5735:646255] 任务2:中第2任务---当前线程:{number = 3, name = (null)}--当前队列:0x604000153820
2018-07-19 01:53:55.850663+0800 01-GCD[5735:646211] 任务3:{number = 1, name = main}--当前队列:0x109aa8e80
2018-07-19 01:53:55.851082+0800 01-GCD[5735:646255] 任务2:中第3任务---当前线程:{number = 3, name = (null)}--当前队列:0x604000153820
2018-07-19 01:53:55.851373+0800 01-GCD[5735:646211] 任务3:{number = 1, name = main}--当前队列:0x109aa8e80
2018-07-19 01:53:55.852096+0800 01-GCD[5735:646255] 任务2:中第4任务---当前线程:{number = 3, name = (null)}--当前队列:0x604000153820
2018-07-19 01:53:55.852750+0800 01-GCD[5735:646211] 任务3:{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---{number = 3, name = (null)}
2018-07-19 22:30:25.586603+0800 02-队列组[1481:70416] 任务2---{number = 4, name = (null)}
2018-07-19 22:30:25.586828+0800 02-队列组[1481:70343] 任务1---{number = 3, name = (null)}
2018-07-19 22:30:25.586848+0800 02-队列组[1481:70416] 任务2---{number = 4, name = (null)}
2018-07-19 22:30:25.587274+0800 02-队列组[1481:70343] 任务1---{number = 3, name = (null)}
2018-07-19 22:30:25.587307+0800 02-队列组[1481:70416] 任务2---{number = 4, name = (null)}
2018-07-19 22:30:25.587445+0800 02-队列组[1481:70343] 任务1---{number = 3, name = (null)}
2018-07-19 22:30:25.588589+0800 02-队列组[1481:70416] 任务2---{number = 4, name = (null)}
2018-07-19 22:30:25.590428+0800 02-队列组[1481:70343] 任务1---{number = 3, name = (null)}
2018-07-19 22:30:25.591197+0800 02-队列组[1481:70416] 任务2---{number = 4, name = (null)}
2018-07-19 22:30:25.591991+0800 02-队列组[1481:70295] 任务3---{number = 1, name = main}
2018-07-19 22:30:25.592272+0800 02-队列组[1481:70295] 任务3---{number = 1, name = main}
2018-07-19 22:30:25.592861+0800 02-队列组[1481:70295] 任务3---{number = 1, name = main}
2018-07-19 22:30:25.593492+0800 02-队列组[1481:70295] 任务3---{number = 1, name = main}
2018-07-19 22:30:25.595441+0800 02-队列组[1481:70295] 任务3---{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

你可能感兴趣的:(多线程(一))