GCD(II)
主队列
组队列是串行队列,全局队列属于并发队列。
同步执行 + 主队列
同步执行 + 主队列
在不同线程中调用结果也是不一样,在主线程中调用会出现死锁,而在其他线程中则不会。
在主线程中调用同步执行 + 主队列
互相等待卡住不可行
1 - (void)syncMain { 2 3 NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 4 NSLog(@"syncMain---begin"); 5 6 dispatch_queue_t queue = dispatch_get_main_queue(); 7 8 dispatch_sync(queue, ^{ 9 // 追加任务1 10 for (int i = 0; i < 2; ++i) { 11 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 12 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 13 } 14 }); 15 16 dispatch_sync(queue, ^{ 17 // 追加任务2 18 for (int i = 0; i < 2; ++i) { 19 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 20 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 21 } 22 }); 23 24 dispatch_sync(queue, ^{ 25 // 追加任务3 26 for (int i = 0; i < 2; ++i) { 27 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 28 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 29 } 30 }); 31 32 NSLog(@"syncMain---end"); 33 }
打印结果:
1 2018-03-28 10:58:43.954991+0800 StruggleSwift[1461:28157] currentThread---<NSThread: 0x60c000074d40>{number = 1, name = main} 2 2018-03-28 10:58:44.592754+0800 StruggleSwift[1461:28157] syncMain---begin 3 (lldb)
死锁图:
现象:
在主线程中使用同步执行 + 主队列
,追加到主线程的任务1、任务2、任务3都不再执行了,而且syncMain---end
也没有打印,在XCode 9上还会报崩溃。这是为什么呢?
原因:是queue 和 任务 1相互等待造成的。
我们在主线程中执行syncMain
方法,相当于把syncMain
任务放到了主线程的队列中。而同步执行
会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把任务1
追加到主队列中,任务1
就在等待主线程处理完syncMain
任务。而syncMain
任务需要等待任务1
执行完毕,才能接着执行。
那么,现在的情况就是syncMain
任务和任务1
都在等对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务执行不了,而且syncMain---end
也没有打印。
解决办法:将同步sync 改为 async。
主队列和 任务1 必须等待主线程中的任务走完才能走添加到主队列中的任务。
其他线程中调用同步执行 + 主队列
1 //其他线程中调用 同步执行 + 主队列 2 - (void)otherThreadExecuteForSyncMain { 3 // 使用 NSThread 的 detachNewThreadSelector 方法会创建线程,并自动启动线程执行 selector 任务 4 [NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil]; 5 } 6 7 8 //主线程中调用 同步执行 + 主队列 9 - (void)syncMain { 10 11 NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 12 NSLog(@"syncMain---begin"); 13 14 dispatch_queue_t queue = dispatch_get_main_queue(); 15 16 dispatch_sync(queue, ^{ 17 // 追加任务1 18 for (int i = 0; i < 2; ++i) { 19 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 20 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 21 } 22 }); 23 24 dispatch_sync(queue, ^{ 25 // 追加任务2 26 for (int i = 0; i < 2; ++i) { 27 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 28 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 29 } 30 }); 31 32 dispatch_sync(queue, ^{ 33 // 追加任务3 34 for (int i = 0; i < 2; ++i) { 35 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 36 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 37 } 38 }); 39 40 NSLog(@"syncMain---end"); 41 }
打印结果:
1 <-------------------------线程结束--------------------------> 2 2018-03-28 14:07:48.676276+0800 StruggleSwift[4834:178554] [BoringSSL] Function boringssl_session_errorlog: line 2871 [boringssl_session_read] SSL_ERROR_ZERO_RETURN(6): operation failed because the connection was cleanly shut down with a close_notify alert 3 2018-03-28 14:07:48.678295+0800 StruggleSwift[4834:179375] currentThread---<NSThread: 0x60c000272bc0>{number = 7, name = (null)} 4 2018-03-28 14:07:48.680254+0800 StruggleSwift[4834:178554] [BoringSSL] Function boringssl_session_errorlog: line 2871 [boringssl_session_read] SSL_ERROR_ZERO_RETURN(6): operation failed because the connection was cleanly shut down with a close_notify alert 5 2018-03-28 14:07:49.436085+0800 StruggleSwift[4834:179375] syncMain---begin 6 2018-03-28 14:08:12.947550+0800 StruggleSwift[4834:178356] 1---<NSThread: 0x6000000760c0>{number = 1, name = main} 7 2018-03-28 14:08:17.442346+0800 StruggleSwift[4834:178356] 1---<NSThread: 0x6000000760c0>{number = 1, name = main} 8 2018-03-28 14:08:28.777947+0800 StruggleSwift[4834:178356] 2---<NSThread: 0x6000000760c0>{number = 1, name = main} 9 2018-03-28 14:08:30.779303+0800 StruggleSwift[4834:178356] 2---<NSThread: 0x6000000760c0>{number = 1, name = main} 10 2018-03-28 14:08:37.444289+0800 StruggleSwift[4834:178356] 3---<NSThread: 0x6000000760c0>{number = 1, name = main} 11 2018-03-28 14:08:39.445499+0800 StruggleSwift[4834:178356] 3---<NSThread: 0x6000000760c0>{number = 1, name = main} 12 2018-03-28 14:08:40.512798+0800 StruggleSwift[4834:179375] syncMain---end
结论:
- 所有任务都是在主线程(非当前线程)中执行的,没有开启新的线程(所有放在
主队列
中的任务,都会放到主线程中执行)。 - 所有任务都在打印的
syncConcurrent---begin
和syncConcurrent---end
之间执行(同步任务
需要等待队列的任务执行结束)。 - 任务是按顺序执行的(主队列是
串行队列
,每次只有一个任务被执行,任务一个接一个按顺序执行)。
为什么现在就不会卡住了呢?
因为syncMain 任务
放到了其他线程里,而任务1
、任务2
、任务3
都在追加到主队列中,这三个任务都会在主线程中执行。syncMain 任务
在其他线程中执行到追加任务1
到主队列中,因为主队列现在没有正在执行的任务,所以,会直接执行主队列的任务1
,等任务1
执行完毕,再接着执行任务2
、任务3
。所以这里不会卡住线程。
异步执行 + 主队列
1 // 异步执行 + 主队列 2 gcd.asyncMain() 3 4 print("<-------------------------线程结束-------------------------->") 5 6 7 //异步执行 + 主队列 8 - (void)asyncMain { 9 NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程 10 NSLog(@"asyncMain---begin"); 11 12 dispatch_queue_t queue = dispatch_get_main_queue(); 13 14 dispatch_async(queue, ^{ 15 // 追加任务1 16 for (int i = 0; i < 2; ++i) { 17 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 18 NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程 19 } 20 }); 21 22 dispatch_async(queue, ^{ 23 // 追加任务2 24 for (int i = 0; i < 2; ++i) { 25 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 26 NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程 27 } 28 }); 29 30 dispatch_async(queue, ^{ 31 // 追加任务3 32 for (int i = 0; i < 2; ++i) { 33 [NSThread sleepForTimeInterval:2]; // 模拟耗时操作 34 NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程 35 } 36 }); 37 38 NSLog(@"asyncMain---end"); 39 }
打印结果:
1 2018-03-28 16:51:52.494812+0800 StruggleSwift[11744:343710] currentThread---<NSThread: 0x604000261a00>{number = 1, name = main} 2 2018-03-28 16:51:52.495049+0800 StruggleSwift[11744:343710] asyncMain---begin 3 2018-03-28 16:52:01.891018+0800 StruggleSwift[11744:343710] asyncMain---end 4 <-------------------------线程结束--------------------------> 5 2018-03-28 16:52:01.891399+0800 StruggleSwift[11744:344217] [BoringSSL] Function boringssl_session_errorlog: line 2871 [boringssl_session_read] SSL_ERROR_ZERO_RETURN(6): operation failed because the connection was cleanly shut down with a close_notify alert 6 2018-03-28 16:52:01.891690+0800 StruggleSwift[11744:344217] [BoringSSL] Function boringssl_session_errorlog: line 2871 [boringssl_session_read] SSL_ERROR_ZERO_RETURN(6): operation failed because the connection was cleanly shut down with a close_notify alert 7 2018-03-28 16:52:10.303745+0800 StruggleSwift[11744:343710] 1---<NSThread: 0x604000261a00>{number = 1, name = main} 8 2018-03-28 16:52:12.305159+0800 StruggleSwift[11744:343710] 1---<NSThread: 0x604000261a00>{number = 1, name = main} 9 2018-03-28 16:52:17.683466+0800 StruggleSwift[11744:343710] 2---<NSThread: 0x604000261a00>{number = 1, name = main} 10 2018-03-28 16:52:19.684801+0800 StruggleSwift[11744:343710] 2---<NSThread: 0x604000261a00>{number = 1, name = main} 11 2018-03-28 16:52:24.047373+0800 StruggleSwift[11744:343710] 3---<NSThread: 0x604000261a00>{number = 1, name = main} 12 2018-03-28 16:52:26.047712+0800 StruggleSwift[11744:343710] 3---<NSThread: 0x604000261a00>{number = 1, name = main}
打印结果看本质:
- 所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然
异步执行
具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中)。 - 所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。
- 任务是按顺序执行的(因为主队列是
串行队列
,每次只有一个任务被执行,任务一个接一个按顺序执行)。