一、常见的多线程
1、pthread
- pthread是一套通用的多线程API,适用于Unix\Linux\Windows等系统
- 跨平台,是使用C语言编写的API所以使用难度大
- 线程的生命周期需要程序员管理
2、NSThread
- 是使用OC语言编写,使用起来更加面向对象,简单易用,可直接操作线程对象
- 线程的生命周期需要程序员管理
3、GCD
- 是使用C语言编写,为了替代NSTread等线程计算而生,充分利用了设备的多核
- 线程的生命周期自动管理
4、NSOperation
- 是对GCD的封装,比GCD多了一些更加简单实用的功能,使用更加面向对象
- 线程的生命周期自动管理
二、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
崩溃
- 崩溃信息
分析:通过代码和打印信息可以知道,代码是在主线程上执行的,所以会把任务1、dispatch_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在另外一个队列中执行
- 为什么同步串行队列不会造成死锁
- 分析:从打印可以知道,任务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有开线程的能力,但是如果是在非主队列时,开线程
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有开线程的能力
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)
- 子线程死锁分析
四、小结
- 1、同步和异步主要影响:能不能开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力 - 2、并发和串行主要影响:任务的执行方式
并发:多个任务并发(同时)执行
串行:一个任务执行完毕后,再执行下一个任务 - 3、使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)
五、队列组
- 队列组可以实现多个任务的执行顺序可控
- 异步并发执行任务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