本文着重点是任务和队列,线程是什么?多线程有什么用?怎么正确使用任务和队列?案例使用GCD讲解。
线程是进程的执行单元,一个进程至少有一个线程。
多线程的优点: 能适当提高程序的执行效率 能适当提高资源利用率(CPU、内存利用率);
多线程的缺点: 开启线程需要占用一定的内存空间(默认情况下,主线程占用 1M,子线程占用 512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能,CPU会在N多线程之间调度,消耗大量的CPU资源,每条线程被调度执行的频次会降低。
Block
中的业务代码执行完毕 dispatch
函数才会返回,然后当前线程才会继续往下运行。dispatch
函数会立即返回, 然后Block
在后台异步执行,即当前线程会直接往下执行,不会阻塞当前线程。线性表
,采用 FIFO(先进先出)
的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。分为串行队列和并行队列。
Block
中的业务代码执行完毕),才能执行下一个任务。Block
中的业务代码执行完毕)。(可以开启多个线程,并且同时执行任务)。并发队列的并发功能只有在异步dispatch_async
函数下才有效。串行队列里面只会有一个线程,按序执行。
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
并发队列可以存在多个线程。
//创建并行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
本质是一个并发队列,由系统提供,方便编程,可以不用创建就直接使用。
//获取全局并发队列
/**
第一个参数:优先级 也可直接填后面的数字
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
第二个参数: 预留参数 0
*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
本质是一个串行队列,专门负责调度主线程,添加到主队列的任务不会开启新的线程。
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
以上都是对线程的理解,执行任务需要开启线程,线程在队列里面按序执行,接下来我们看看实际的应用
任务 | 并发队列 | 串行队列 | 主队列 |
---|---|---|---|
同步(sync) | 没有开启新线程 串行执行任务 | 没有开启新线程 串行执行任务 | 串行执行任务 死锁卡住不执行 |
异步(async) | 有开启新线程 并发执行任务 | 有开启新线程(1条) 串行执行任务 | 没有开启新线程 串行执行任务 |
这是六种最基本的排列组合,我们一一讲解。
- (void)syncConcurrent {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:syncConcurrent---Begin");
//创建并行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
// 任务1
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 任务2
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 任务3
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"wuwuFQ:syncConcurrent---End");
}
首先明确一点,同步(sync)会阻塞线程等待block回调,并且是不具备开启线程能力的,即使放在并发队列里面,还是需要当前线程也就是主线程去执行任务的
所以结论是:并发+同步 = 没有开启新线程,串行执行任务
- (void)asyncConcurrent {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:asyncConcurrent---Begin");
//创建并行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 任务1
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 任务2
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 任务3
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"wuwuFQ:asyncConcurrent---End");
}
这个组合是我们开发过程中经常使用的,异步(async)开启了新线程,并行队列不会阻塞线程,不会等待block的回调,CPU在几个线程之间快速切换,我运行了多次,每次的执行顺序都不通。
所以结论是:并发+异步 = 开启了新线程,并发执行任务
- (void)syncSerial {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:syncSerial---Begin");
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
// 任务1
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 任务2
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 任务3
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
}
});
// 任务4
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:4---%@",[NSThread currentThread]); // 打印当前线程
}
NSLog(@"wuwuFQ:syncSerial---End");
}
同步不具备开启新线程能力,任务都在主线程按序执行
所以结论是:串行+同步 = 没有开启新线程,串行执行任务
- (void)asyncSerial {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:asyncSerial---Begin");
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 任务1
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 任务2
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 任务3
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
}
});
// 任务4
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:4---%@",[NSThread currentThread]); // 打印当前线程
}
NSLog(@"wuwuFQ:asyncSerial---End");
}
异步具有开启新线程的能力,但是在串行队列里面任务1、2、3只开启了一个线程,任务1、2、3在子线程按顺序执行,任务4在主线程里面,CPU在两个线程之间快速切换,所以任务4的打印会穿插在任务1、2、3中间,但任务1、2、3的执行顺序不会变。
所以结论是:串行+异步 = 开启了一个新线程,串行执行任务
- (void)syncMainQueue {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:syncMainQueue---Begin");
//创建并行队列
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_sync(main_queue, ^{
// 任务1
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(main_queue, ^{
// 任务2
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(main_queue, ^{
// 任务3
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"wuwuFQ:syncMainQueue---End");
}
主队列负责管理主线程,不会创建新的线程,创建的1、2、3任务遵循
FIFO(先进先出)
插入到队尾最后执行,但是任务是同步执行,所以后面的任务需要等待任务1、2、3的执行,而任务1、2、3却在队列的队尾要等待前面的任务执行,这样线程就会卡主,造成死锁。而程序也会报错:__DISPATCH_WAIT_FOR_QUEUE__
所以结论是:主队列+同步 = 阻塞主线程,死锁
- (void)asyncMainQueue {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印当前线程
NSLog(@"wuwuFQ:asyncMainQueue---Begin");
//创建并行队列
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(main_queue, ^{
// 任务1
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(main_queue, ^{
// 任务2
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(main_queue, ^{
// 任务3
for (int i = 0; i < 2; ++i) {
NSLog(@"wuwuFQ:3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"wuwuFQ:asyncMainQueue---End");
}
主队列不会创建新的线程,首先任务1、2、3会被插入到主队列尾部,然后任务异步执行,无需等待block回调,主线程按序执行,最后执行任务1、2、3。
所以结论是:主队列+异步 = 没有开启新线程,串行执行任务