线程与队列说不清道不明的关系:
线程是代码执行的路径,队列则是用于保存以及管理任务的,线程负责去队列中取任务进行执行。 我的理解:多个队列的任务可以在一条线程上执行,一个队列的任务也可以在多条线程上执行。个人理解,队列可以包含线程,线程也可以包含队列。
dispatch_sync
:立马在当前线程执行任务,执行完再往下走,这句话就可以解释很多问题。
dispatch_async
:不要求立马在当前线程执行任务,可能会开启新线程,也有可能不会。
一、画图解释下队列跟线程间的关系
1、一个队列对应一个线程
"主队列" 对应 "主线程"
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blueColor];
[self threadTest];
}
- (void)threadTest{
NSLog(@"我是任务1");
NSLog(@"我是任务2");
}
复制代码
2、一个队列对应两个线程
"队列1" 对应 "主线程"和"新线程" (因为主队列没有开启新线程的能力所以用"队列1")
- (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);
}
复制代码
2018-06-20 14:40:05.904914+0800 WLZCyclycReference[4497:158751] 任务1
2018-06-20 14:40:05.905099+0800 WLZCyclycReference[4497:158751] currentThread == <NSThread: 0x60800007c000>{number = 1, name = main}
2018-06-20 14:40:05.905230+0800 WLZCyclycReference[4497:158829] 任务2
2018-06-20 14:40:05.905381+0800 WLZCyclycReference[4497:158829] currentThread == <NSThread: 0x608000270800>{number = 3, name = (null)}
2018-06-20 14:40:08.906308+0800 WLZCyclycReference[4497:158751] 方法执行结束
复制代码
3、两个队列对应一个线程
"主队列"和"队列1" 对应 "主线程"
- (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);
}
复制代码
2018-06-20 14:53:39.210769+0800 WLZCyclycReference[4741:169494] 我是任务2
2018-06-20 14:53:39.210959+0800 WLZCyclycReference[4741:169494] currentThread == <NSThread: 0x608000065640>{number = 1, name = main}
2018-06-20 14:53:42.212170+0800 WLZCyclycReference[4741:169494] 我是任务1
2018-06-20 14:53:42.212615+0800 WLZCyclycReference[4741:169494] currentThread == <NSThread: 0x608000065640>{number = 1, name = main}
2018-06-20 14:53:42.212810+0800 WLZCyclycReference[4741:169494] 方法执行结束
复制代码
4、两个队列对应两个线程
"主队列"和"队列1" 对应 "主线程"和"新线程"
- (void)threadTest{
dispatch_queue_t queue = dispatch_queue_create("队列1", DISPATCH_QUEUE_SERIAL);
NSLog(@"我是任务2");
[self getCurrentThread];
//开启新线程
dispatch_async(queue, ^{
NSLog(@"我是任务1");//1️⃣
[self getCurrentThread];
});
NSLog(@"方法执行结束");//2️⃣
//1️⃣2️⃣的执行先后顺序是随机的
}
- (void)getCurrentThread{
NSThread *currentThread = [NSThread currentThread];
NSLog(@"currentThread == %@",currentThread);
}
复制代码
2018-06-20 15:11:12.132538+0800 WLZCyclycReference[5076:184244] 我是任务2
2018-06-20 15:11:12.132705+0800 WLZCyclycReference[5076:184244] currentThread == <NSThread: 0x60400007bd80>{number = 1, name = main}
2018-06-20 15:11:12.132809+0800 WLZCyclycReference[5076:184244] 方法执行结束
2018-06-20 15:11:12.132823+0800 WLZCyclycReference[5076:184332] 我是任务1
2018-06-20 15:11:12.133009+0800 WLZCyclycReference[5076:184332] currentThread == <NSThread: 0x6080002602c0>{number = 3, name = (null)}
复制代码
二、主线程特点讲解
主线程特点:如果主线程里有任务就必须等主线程任务执行完才轮到主队列(如果是其他队列的任务,那么任务就不用等待,会直接被主线程执行)的。所以说如果在主队列异步(开启新线程)执行任务无所谓,但是如果在主队列同步(不开启新线程,需要在主线程执行)执行任务会循环等待,造成死锁(但是在一般串行队列这样执行就不会出问题,一切都是因为主线程的这个特点)。
1、主线程特点演示
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
NSLog(@"1");
NSThread *mainThread = [NSThread currentThread];
NSLog(@"currentThread == %@",mainQueue);
sleep(3);
NSLog(@"2");
});
NSLog(@"主线程执行完了");
复制代码
2018-06-20 12:09:05.598367+0800 WLZCyclycReference[3254:101260] 主线程执行完了
2018-06-20 12:09:05.601950+0800 WLZCyclycReference[3254:101260] 1
2018-06-20 12:09:05.602095+0800 WLZCyclycReference[3254:101260] currentThread == <OS_dispatch_queue_main: com.apple.main-thread>
2018-06-20 12:09:08.603048+0800 WLZCyclycReference[3254:101260] 2
复制代码
从执行结果可以完美的看出主线程的特点,所以我严重怀疑:主队列的任务只能在主线程上执行,换句话说就是主队列是串行队列的阉割版,即主队列没有创建新线程的能力。
主线程在执行主队列任务的过程中:
- 如果主队列通过异步的方式添加"任务1",那么既然主队列没有开新线程的能力,就等我主线程把我当前的任务执行完了,我主线程再去执行"任务1";
- 如果主队列通过同步的方式添加"任务2",那么我主线程还是要先执行完我当前的任务,但是"任务2"是同步的,"任务2"又必须要主线程执行自己。那主线程只好循环等待然后死了。
2、主线程死锁演示,主线程产生死锁针对的是主队列
//主队列 同步执行任务 死锁
dispatch_sync(dispatch_get_main_queue(), ^{
});
复制代码
3、主线程中,同样的任务放到其它队列(串行、并行均可)同步执行,不会死锁,并且顺序执行,执行完“串行队列1”的任务,继续向下执行主队列的任务。
dispatch_queue_t queue = dispatch_queue_create("串行队列1", DISPATCH_QUEUE_SERIAL);
//串行队列 同步执行任务 因为没有开启线程所以还是主线程
dispatch_sync(queue, ^{
});
复制代码
上述代码中"主队列"、"串行队列1"两个队列的任务互不干扰,这种情况其实就相当于把两个队列放到了一个更大的虚拟并行队列中,可以同时执行任务
FIFO,串行队列任务1没有执行完,同步执行任务2的话绝逼死锁,但是死锁产生的原因是同一个队列里边的两个任务相互等待,如果不是同一个队列,那么久不会产生死锁。
4、同一个串行队列,同步执行任务嵌套,造成死锁。
dispatch_queue_t queue = dispatch_queue_create("串行队列", DISPATCH_QUEUE_SERIAL);
//串行队列 同步执行任务 因为没有开启线程所以还是主线程
dispatch_sync(queue, ^{
NSLog(@"1");
dispatch_sync(queue, ^{
NSLog(@"串行队列嵌套执行");
});
NSLog(@"2");
});
复制代码
上边死锁的情况可以说明一个问题,某一个串行队列正在执行一个同步任务,这时候又在当前队列插入了一个同步任务就会造成死锁。这种情况就跟1(主队列有任务,主线程执行ing,主队列插入同步任务造成死锁)的情况是一样的,只不过主队列是系统自带的,我们这个队列是自己创建的而已。
同步异步:指的是函数(方法),能否开启新的线程。同步不能,异步可以
串行并行:指的是队列,任务的执行方式,串行指各个任务按顺序执行。并行指多个任务可以同时执行
串行队列也可以开启新的线程,只不过开启之后也只是按顺序执行,
三、概念解释
以下概念参考"08号疯子"的博客: IOS多线程知识总结/队列概念/GCD/串行/并行/同步/异步
-
进程:正在进行中的程序被称为进程,负责程序运行的内存分配;每一个进程都有自己独立的虚拟内存空间;
-
线程:线程是进程中一个独立的执行路径(控制单元);一个进程中至少包含一条线程,即主线程。
-
队列:
dispatch_queue_t
,一种先进先出的数据结构,线程的创建和回收不需要程序员操作,由队列负责。 -
队列-串行队列:队列中的任务只会顺序执行
dispatch_queue_t q = dispatch_queue_create(“....”, dispatch_queue_serial);
- 队列-并行队列:队列中的任务通常会并发执行
dispatch_queue_t q = dispatch_queue_create("......", dispatch_queue_concurrent);
- 队列-全局队列:是系统开发的,直接拿过来(get)用就可以;与并行队列类似,但调试时,无法确认操作所在队列
dispatch_queue_t q = dispatch_get_global_queue(dispatch_queue_priority_default, 0);
- 队列-主队列:每一个应用程序对应唯一一个主队列,直接get即可;在多线程开发中,使用主队列更新UI
dispatch_queue_t q = dispatch_get_main_queue();
-
串行队列同步:操作不会新建线程、操作顺序执行;
-
串行队列异步:操作需要一个子线程,会新建线程、线程的创建和回收不需要程序员参与,操作顺序执行,是最安全的选择;
-
并行队列同步:操作不会新建线程、操作顺序执行;
-
并行队列异步:操作会新建多个线程(有多少任务,就开n个线程执行)、操作无序执行;队列前如果有其他任务,会等待前面的任务完成之后再执行;场景:既不影响主线程,又不需要顺序执行的操作
-
全局队列同步:操作不会新建线程、操作顺序执行;
-
全局队列异步:操作会新建多个线程、操作无序执行,队列前如果有其他任务,会等待前面的任务完成之后再执行;
-
主局队列同步:如果把主线程中的操作看成一个大的block,那么除非主线程被用户杀掉,否则永远不会结束;主队列中添加的同步操作永远不会被执行,会死锁;
-
主局队列异步:操作都应该在主线程上顺序执行的,不存在异步的;
1、队列和线程的区别:
队列:是管理线程的,相当于线程池,能管理线程什么时候执行。
队列分为串行队列和并行队列:
串行队列:队列中的线程按顺序执行(不会同时执行)
并行队列:队列中的线程会并发执行,可能会有一个疑问,队列不是先进先出吗,如果后面的任务执行完了,怎么出去的了。这里需要强调下,任务执行完毕了,不一定出队列。只有前面的任务执行完了,才会出队列。
2、主线程队列和gcd创建的队列也是有区别的。
主线程队列和gcd创建的队列是不同的。在gcd中创建的队列优先级没有主队列高,所以在gcd中的串行队列开启同步任务里面没有嵌套任务是不会阻塞主线程,只有一种可能导致死锁,就是串行队列里,嵌套开启任务,有可能会导致死锁。
主线程队列中不能开启同步,会阻塞主线程。只能开启异步任务,开启异步任务也不会开启新的线程,只是降低异步任务的优先级,让cpu空闲的时候才去调用。而同步任务,会抢占主线程的资源,会造成死锁。
3、线程:里面有非常多的任务(同步,异步)
同步与异步的区别:
同步任务优先级高,在线程中有执行顺序,不会开启新的线程。
异步任务优先级低,在线程中执行没有顺序,看cpu闲不闲。在主队列中不会开启新的线程,其他队列会开启新的线程。
4、主线程队列注意:
在主队列开启异步任务,不会开启新的线程而是依然在主线程中执行代码块中的代码。为什么不会阻塞线程?
主队列开启异步任务,虽然不会开启新的线程,但是他会把异步任务降低优先级,等闲着的时候,就会在主线程上执行异步任务。
在主队列开启同步任务,为什么会阻塞线程?
在主队列开启同步任务,因为主队列是串行队列,里面的线程是有顺序的,先执行完一个线程才执行下一个线程,而主队列始终就只有一个主线程,主线程是不会执行完毕的,因为他是无限循环的,除非关闭应用开发程序。因此在主线程开启一个同步任务,同步任务会想抢占执行的资源,而主线程任务一直在执行某些操作,不肯放手。两个的优先级都很高,最终导致死锁,阻塞线程了。