队列与任务
任务task
就是需要执行的操作,是GCD中放在block中,需要在线程中执行的那段代码。
执行方式,有两种:
-
同步执行
把任务同步添加到指定的队列中。在队列中,之前的任务执行结束之前会一直等待,直到队列里面的任务完成之后,再去执行其他的任务
同步执行的任务只能在当前线程中执行,不具备开启新线程的能力。
-
函数:
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
// 同步执行任务的创建 dispatch_sync(queue, ^{ // 同步执行的任务代码 });
-
异步执行
异步添加任务到指定的队列中,不需要理会队列中其他的任务。这种任务无需做任何的等待,添加到队列就会立即执行。
异步执行可以在新的线程中执行,具备开启新的线程的能力。
-
函数:
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
// 异步执行任务的创建 dispatch_async(queue, ^{ // 异步执行的任务代码 });
队列
是指执行任务的等待队列,是用来存放任务的,队列分为“串行队列”和“并发队列”。队列采用“先进先出”的原则,即新任务总是被出入到队列的末尾,而读取任务的时候总是从队列的头部开始。串行队列和并发队列都遵循这个原则。
队列的分类
-
串行队列(serial)
只开启一个线程
一次只有一个任务被执行
一个任务执行完成后,再去执行下一个任务
多个串行队列之间是按照并发队列的形式进行执行
-
并发队列(concurrent)
可以开启多个线程
多个任务可以同时执行
队列的类型以及获取相应的队列
主队列(Main Dispatch Queue)
专门负责调度主线程的任务,没有办法开辟新的线程。在主队列的任务,无论是同步任务还是异步任务,都不会开启新的线程。
-
主线程只能过去,不能创建。获取方法:
dispatch_queue_main_t dispatch_get_main_queue(void)
//获取主队列 dispatch_queue_t mainQueue = dispatch_get_main_queue();
主队列是一种串行队列。
-
主队列与主线程的关系
主队列的任务一定在主线程中执行
主线程可以执行主队列以外的其他队列的任务
所有追加到主队列的操作都会在runloop中执行.
全局并发队列(Global Dispatch Queue
是一种并发队列,有系统提供,方便开发人员编程使用,可以不用创建直接使用。
并发的执行多个任务,但是任务的执行顺序是随机的
-
获取方法:
dispatch_queue_global_t dispatch_get_global_queue(intptr_t identifier, uintptr_t flags);
参数1: 长整型参数intptr_t identifier,表示队列的优先级或者Qos的值
-
参数2: 长整型参数uintptr_t flags,传0就可以。
//创建全局并发队列 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
自定义队列
如果以上两种队列不能满足要求的话,可以自定义队列。
串行队列、并发队列都可以进行自定义
自定义队列可以设置队列的优先级
-
自定义队列需要用到的方法是:
dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);
参数1: 队列的标识符
-
参数2: 一个结合了服务质量Qos级别的队列属性值,可以传以下两种类型的值
-
表示队列类型的值
- 直接传值NULL: 默认创建一个串行队列
dispatch_queue_t queue = dispatch_queue_create("identifier", NULL);
- DISPATCH_QUEUE_SERIAL: 创建一个串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("identifier", DISPATCH_QUEUE_SERIAL);
- DISPATCH_QUEUE_CONCURRENT: 创建一个并发队列
dispatch_queue_t conQueue = dispatch_queue_create("identifier", DISPATCH_QUEUE_CONCURRENT);
-
表示执行任务的服务质量Qos级别
- 使用
dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t _Nullable attr,dispatch_qos_class_t qos_class, int relative_priority);
//设置队列的类型,设置队列的优先级 /* 第二个参数 __QOS_ENUM(qos_class, unsigned int, QOS_CLASS_USER_INTERACTIVE __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x21, QOS_CLASS_USER_INITIATED __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x19, QOS_CLASS_DEFAULT __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x15, QOS_CLASS_UTILITY __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x11, QOS_CLASS_BACKGROUND __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x09, QOS_CLASS_UNSPECIFIED __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x00, ); */ dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, -1); //创建队列 dispatch_queue_t queue = dispatch_queue_create("identifier", attr);
- 使用
-
任务和队列的组合,对线程的影响
任何和队列需要搭配使用,算上主线程的特殊情况,总共是有六种组合。
同步任务 + 并发队列
同步任务不会开启新的线程,虽然任务是在并发队列中,但是系统只会使用同步任务所在的线程,所以这种组合没有开启线程,并且任务按照串行的方式执行。
需要说明的是,这种组合是在当前线程执行任务,不一定是在主线程。
//同步任务 + 并发队列
- (void)concurrentQueueAndSync{
//创建并发队列
dispatch_queue_t queue = dispatch_queue_create("concurrentQueueAndSync", DISPATCH_QUEUE_CONCURRENT);
//创建同步执行的任务1
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"任务1--同步任务 + 并发队列 -: %@",[NSThread currentThread]);
});
//任务2
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"任务2--同步任务 + 并发队列 -: %@",[NSThread currentThread]);
});
//任务3
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"任务3--同步任务 + 并发队列 -: %@",[NSThread currentThread]);
});
//任务4
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"任务4--同步任务 + 并发队列 -: %@",[NSThread currentThread]);
});
}
打印结果为:
2022-04-01 16:28:56.369531+0800 suanfaProject[5147:205112] 任务1--同步任务 + 并发队列 -: <_NSMainThread: 0x60000012c000>{number = 1, name = main}
2022-04-01 16:28:58.370188+0800 suanfaProject[5147:205112] 任务2--同步任务 + 并发队列 -: <_NSMainThread: 0x60000012c000>{number = 1, name = main}
2022-04-01 16:28:59.371567+0800 suanfaProject[5147:205112] 任务3--同步任务 + 并发队列 -: <_NSMainThread: 0x60000012c000>{number = 1, name = main}
2022-04-01 16:29:01.373276+0800 suanfaProject[5147:205112] 任务4--同步任务 + 并发队列 -: <_NSMainThread: 0x60000012c000>{number = 1, name = main}
同步任务 + 串行队列
同步任务不会开启新的线程,而且串行队列也不会开启新的线程。所以这种组合是不会开启线程,并且所有的任务按照串行的方式,按顺序逐个完成。
需要说明的是,这种组合是在当前线程执行任务,不一定是在主线程。
//同步任务 + 串行队列
- (void)serialQueueAndSync{
//创建串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueueAndSync", DISPATCH_QUEUE_SERIAL);
//开启同步执行任务1
dispatch_sync(serialQueue, ^{
sleep(1);
NSLog(@"任务1--同步任务 + 串行队列 -: %@",[NSThread currentThread]);
});
//任务2
dispatch_sync(serialQueue, ^{
sleep(2);
NSLog(@"任务2--同步任务 + 串行队列 -: %@",[NSThread currentThread]);
});
//任务3
dispatch_sync(serialQueue, ^{
sleep(1);
NSLog(@"任务3--同步任务 + 串行队列 -: %@",[NSThread currentThread]);
});
//任务4
dispatch_sync(serialQueue, ^{
sleep(2);
NSLog(@"任务4--同步任务 + 串行队列 -: %@",[NSThread currentThread]);
});
}
打印结果为:
2022-04-01 16:37:14.093280+0800 suanfaProject[5295:212370] 任务1--同步任务 + 串行队列 -: <_NSMainThread: 0x600001194940>{number = 1, name = main}
2022-04-01 16:37:16.094911+0800 suanfaProject[5295:212370] 任务2--同步任务 + 串行队列 -: <_NSMainThread: 0x600001194940>{number = 1, name = main}
2022-04-01 16:37:17.095654+0800 suanfaProject[5295:212370] 任务3--同步任务 + 串行队列 -: <_NSMainThread: 0x600001194940>{number = 1, name = main}
2022-04-01 16:37:19.098231+0800 suanfaProject[5295:212370] 任务4--同步任务 + 串行队列 -: <_NSMainThread: 0x600001194940>{number = 1, name = main}
同步任务 + 主队列
介绍主队列的时候说过,主队列是没有办法开辟新的线程,无论任务是要同步执行还是异步执行,都不会开启新的线程。所以涉及到主线程的组合,是比较特殊的。
这种情况的组合,会阻塞主线程,导致程序的崩溃。究其原因是因为主线程需要等到主线程的任务完成之后,才会去执行主队列的任务。而同步任务(dispatch_sync)函数中的代码需要等到执行完成才会返回。这种情况下,双方互相等待,也就是主队列中的任务等待主线程执行完成,主队列在等待dispatch_sync函数中的任务执行完成,最终结果就是导致主线程卡住。
//同步任务 + 主线程主队列
-(void)mainQueueAndSync{
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//创建异步任务
dispatch_sync(mainQueue, ^{
NSLog(@"同步任务 + 主线程主队列 -: %@",[NSThread currentThread]);
});
}
异步任务 + 并发队列
异步任务会开启新的线程,并且并发任务也会开启新的线程。所以这种组合是多个线程并发执行任务。
//异步任务 + 并发队列
- (void)concurrentQueueAndAsync{
NSLog(@"当前线程 -: %@",[NSThread currentThread]);
//创建并发队列
dispatch_queue_t queue = dispatch_queue_create("concurrentQueueAndAsync", DISPATCH_QUEUE_CONCURRENT);
//创建同步执行的任务1
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任务1--异步任务 + 并发队列 -: %@",[NSThread currentThread]);
});
//任务2
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任务2--异步任务 + 并发队列-: %@",[NSThread currentThread]);
});
//任务2
dispatch_async(queue, ^{
sleep(2);
NSLog(@"任务3--异步任务 + 并发队列 -: %@",[NSThread currentThread]);
});
//任务2
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任务4--异步任务 + 并发队列 -: %@",[NSThread currentThread]);
});
}
打印结果为:
2022-04-01 17:00:01.138537+0800 suanfaProject[5589:228584] 当前线程 -: <_NSMainThread: 0x6000033dc340>{number = 1, name = main}
2022-04-01 17:00:02.139603+0800 suanfaProject[5589:228706] 任务2--异步任务 + 并发队列-: <NSThread: 0x6000033d1a00>{number = 7, name = (null)}
2022-04-01 17:00:02.143847+0800 suanfaProject[5589:228709] 任务1--异步任务 + 并发队列 -: <NSThread: 0x600003394140>{number = 2, name = (null)}
2022-04-01 17:00:02.143849+0800 suanfaProject[5589:228710] 任务4--异步任务 + 并发队列 -: <NSThread: 0x6000033dd9c0>{number = 4, name = (null)}
2022-04-01 17:00:03.140795+0800 suanfaProject[5589:228708] 任务3--异步任务 + 并发队列 -: <NSThread: 0x600003393100>{number = 6, name = (null)}
异步任务 + 串行队列
异步任务会开启线程,串行队列只在新开启的线程中按照顺序执行。
//异步任务 + 串行队列
- (void)serialQueueAndAsync{
NSLog(@"当前线程 -: %@",[NSThread currentThread]);
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("serialQueueAndAsync", DISPATCH_QUEUE_SERIAL);
//创建异步执行任务1
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任务1--异步任务 + 串行队列 -: %@",[NSThread currentThread]);
});
//任务2
dispatch_async(queue, ^{
sleep(2);
NSLog(@"任务2--异步任务 + 串行队列 -: %@",[NSThread currentThread]);
});
//任务3
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任务3--异步任务 + 串行队列 -: %@",[NSThread currentThread]);
});
//任务4
dispatch_async(queue, ^{
sleep(2);
NSLog(@"任务4--异步任务 + 串行队列 -: %@",[NSThread currentThread]);
});
}
打印结果为:
2022-04-01 17:03:10.569084+0800 suanfaProject[5627:230899] 当前线程 -: <_NSMainThread: 0x600002058900>{number = 1, name = main}
2022-04-01 17:03:11.571632+0800 suanfaProject[5627:231008] 任务1--异步任务 + 串行队列 -: <NSThread: 0x600002000640>{number = 7, name = (null)}
2022-04-01 17:03:13.576875+0800 suanfaProject[5627:231008] 任务2--异步任务 + 串行队列 -: <NSThread: 0x600002000640>{number = 7, name = (null)}
2022-04-01 17:03:14.578503+0800 suanfaProject[5627:231008] 任务3--异步任务 + 串行队列 -: <NSThread: 0x600002000640>{number = 7, name = (null)}
2022-04-01 17:03:16.584066+0800 suanfaProject[5627:231008] 任务4--异步任务 + 串行队列 -: <NSThread: 0x600002000640>{number = 7, name = (null)}
异步任务 + 主队列
介绍主队列的时候说过,主队列是没有办法开辟新的线程,无论任务是要同步执行还是异步执行,都不会开启新的线程。所以涉及到主线程的组合,是比较特殊的。
所以这种组合也是不开启线程,并且在主线程中按照顺序串行执行。
//异步任务 + 主队列
-(void)mainQueueAndAsync{
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//创建异步任务
dispatch_async(mainQueue, ^{
sleep(1);
NSLog(@"1--异步任务 + 主队列 -: %@",[NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
sleep(2);
NSLog(@"2--异步任务 + 主队列 -: %@",[NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
sleep(1);
NSLog(@"3--异步任务 + 主队列 -: %@",[NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
sleep(2);
NSLog(@"4--异步任务 + 主队列 -: %@",[NSThread currentThread]);
});
}
打印结果为:
2022-04-01 17:07:02.329970+0800 suanfaProject[5680:233053] 1--异步任务 + 主队列 -: <_NSMainThread: 0x600002ef0340>{number = 1, name = main}
2022-04-01 17:07:04.331448+0800 suanfaProject[5680:233053] 2--异步任务 + 主队列 -: <_NSMainThread: 0x600002ef0340>{number = 1, name = main}
2022-04-01 17:07:05.332954+0800 suanfaProject[5680:233053] 3--异步任务 + 主队列 -: <_NSMainThread: 0x600002ef0340>{number = 1, name = main}
2022-04-01 17:07:07.333557+0800 suanfaProject[5680:233053] 4--异步任务 + 主队列 -: <_NSMainThread: 0x600002ef0340>{number = 1, name = main}
线程间通信
在开发过程中,主线程进行UI刷新,把图片下载、大量计算、网络请求等一些耗时的操作放在其他的线程,当这些耗时的操作完成后需要将数据同步给UI,主线程刷新UI,那么就要用到线程之间的通讯。
在多个线程之前进行通信时,一定要注意线程相互等待导致的死锁、死循环等问题。
//主要是子线程中通信到主线程
-(void)commucationBetweenThreads{
__block NSInteger value = 0;
//创建一个并发队列或者使用global队列
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建异步任务
dispatch_async(queue, ^{
NSLog(@"任务1----thread: %@",[NSThread currentThread]);
for (int i=0; i<5; i++) {
value += 1;
sleep(1);
}
//创建主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//创建异步任务
dispatch_async(mainQueue, ^{
NSLog(@"任务2----thread: %@,and value is %li",[NSThread currentThread],value);
});
});
}