[iOS] GCD - 线程与队列
文章目录
- [[iOS] GCD - 线程与队列](#[iOS] GCD - 线程与队列)
在 iOS 开发中线程和队列有种说不清道不明的关系。
线程是代码执行的路径,队列则是用于保存以及管理任务的,线程负责去队列中取任务进行执行。 我的理解:多个队列的任务可以在一条线程上执行,一个队列的任务也可以在多条线程上执行。个人理解,队列可以包含线程,线程也可以包含队列。
下面有两个 GCD 相关的函数
dispatch_sync:立马在当前线程执行任务,执行完再往下走,这句话可以解释很多问题。(同步)
dispatch_async:不要求立马在当前线程执行任务,可能会开启新线程,也有可能不会。(异步)
一、线程与队列的关系
1.一个队列对应一个线程
"主队列" 对应 "主线程"
objc
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
[self treadTest];
// Do any additional setup after loading the view.
}
- (void) treadTest {
NSLog(@"我是test1");
NSLog(@"我是test2");
}

2.一个队列对应两个线程
"队列1" 对应 "主线程"和"新线程" (因为主队列没有开启新线程的能力所以用"队列1")
objc
- (void)threadTest{
dispatch_queue_t queue = dispatch_queue_create("队列1", DISPATCH_QUEUE_SERIAL);
//同步 不开启新线程,所以在主线程执行
dispatch_sync(queue, ^{
NSLog(@"任务1");
[self getCurrentThread];
});
//异步 开启新线程
dispatch_async(queue, ^{
NSLog(@"任务2");//2
[self getCurrentThread];
});
sleep(3);//如果没有这个方法,2,3的执行先后顺序是不确定的,因为是两个线程,执行先后没有关系
NSLog(@"方法执行结束");//3
}
- (void)getCurrentThread{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"currentThread == %@",currentThread);
}
任务1
currentThread == <_NSMainThread: 0x600001708000>{number = 1, name = main}
任务2
currentThread == <NSThread: 0x600001720840>{number = 6, name = (null)}
方法执行结束
一个队列里面的两个任务被两个线程所执行,没有所谓的先后顺序,完全取决于他的执行时长,两个任务之前毫无关系。
3.两个队列对应一个线程
objc
- (void) threadTest {
dispatch_queue_t queue = dispatch_queue_create("队列 1", DISPATCH_QUEUE_SERIAL);
NSLog(@"这是任务2");
[self getCurrentThread];
dispatch_sync(queue, ^{
// sleep(3);
NSLog(@"这是任务 1");
[self getCurrentThread];
});
NSLog(@"方法执行结束");
}
- (void)getCurrentThread{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"currentThread == %@",currentThread);
}
这是任务2
currentThread == <_NSMainThread: 0x600001714000>{number = 1, name = main}
这是任务 1
currentThread == <_NSMainThread: 0x600001714000>{number = 1, name = main}
方法执行结束
在这里主线程正在执行主队列的任务 2,在这时队列 1 将任务1插入,主线程执行完任务1 后继续执行主队列的任务。
4.两个队列对应两个线程
objc
- (void) threadTest {
dispatch_queue_t queue = dispatch_queue_create("队列1", DISPATCH_QUEUE_SERIAL);
NSLog(@"任务2");
[self getCurrentThread];
dispatch_async(queue, ^{
// sleep(3);
NSLog(@"任务1");
[self getCurrentThread];
});
NSLog(@"方法执行结束");
}
- (void)getCurrentThread{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"currentThread == %@",currentThread);
}
任务2
currentThread == <_NSMainThread: 0x600001708080>{number = 1, name = main}
方法执行结束
任务1
currentThread == <NSThread: 0x60000170a180>{number = 3, name = (null)}
在这里两个线程和两个队列,他们的执行毫不相干,执行完成顺序完全取决于执行时间的长短。
二、主线程特点导致死锁
主线程特点:如果主线程里有任务就必须等主线程任务执行完才轮到主队列(如果是其他队列的任务,那么任务就不用等待,会直接被主线程执行)的。所以说如果在主队列异步(开启新线程)执行任务无所谓,但是如果在主队列同步(不开启新线程,需要在主线程执行)执行任务会循环等待,造成死锁(但是在一般串行队列这样执行就不会出问题,一切都是因为主线程的这个特点)。
那这里我顺便把死锁说了
1.主线程死锁,这里主要针对的是主队列
这里的死锁是 GCD 所导致的死锁
- 你(主线程 )开始执行
任务 A里的代码。 任务 A执行到一半,你读到了一个特殊的指令(dispatch_sync):- 这个指令说:"请现在往你的任务清单(主队列)的末尾,再加一个
任务 B,并且你(主线程)必须停下手头所有的工作,站在这里,亲眼等到任务 B被完成,然后才能继续做任务 A。 - 你(主线程 )遵从了指令:(在这里导致了死锁的发生 )
- 你把
任务 B写在了任务 A的后面。 - 你停下了 手头正在做的
任务 A,开始傻等。 - 你在等什么?你在等
任务 B被完成。
- 你把
- 任务清单(主队列)现在的状态是:
任务 A(正在执行中,但被卡住了...)任务 B(在后面排队...)
- 悖论出现了:
任务 B怎么样才能被执行?- 它必须等
任务 A执行完毕 。(因为这是个串行清单) 任务 A怎么样才能执行完毕?- 它必须等
任务 B执行完毕 。(因为dispatch_sync命令它等)
所以这就导致了主线程在那循环等死。
下面我给出代码
objc
//主队列 同步执行任务 死锁
dispatch_sync(dispatch_get_main_queue(), ^{
});
2.同一个串行队列,同步执行任务嵌套,造成死锁(补)
objc
dispatch_queue_t queue = dispatch_queue_create("串行队列", DISPATCH_QUEUE_SERIAL);
//串行队列 同步执行任务 因为没有开启线程所以还是主线程
dispatch_sync(queue, ^{
NSLog(@"1");
dispatch_sync(queue, ^{
NSLog(@"串行队列嵌套执行");
});
NSLog(@"2");
});
上面导致的死锁就是,我们在串行队列中执行了一个任务,在这时我们又添加了一个任务造成了死锁,这个和主线程死锁没什么区别,只不过主队列是自带的,这个是我们自己创造的。
同步异步:指的是函数(方法),能否开启新的线程。同步不能,异步可以
串行并行:指的是队列,任务的执行方式,串行指各个任务按顺序执行。并行指多个任务可以同时执行
串行队列也可以开启新的线程,只不过开启之后也只是按顺序执行,
总结
在这里我只介绍了串行和同步的组合方式在后续会继续介绍剩下的三种。这篇文章也主要专注于前半部分的线程和队列关系的理解,后续文章会更加系统的来介绍异步,同步,串行,并行,他们几个之间的组合。