1.GCD基本使用(异步函数+并发队列)
- 可以看到,系统自动给开辟了新的子线程来执行这三个任务,且由于时并发队列,系统开辟了三个子线程
- 注意:开辟多少个子线程由系统决定,三个任务依次从队列中取出放在不同的子线程执行,但是由于CPU调度子线程是由系统控制的,所以任务执行的顺序不定,并没有违背队列中任务的先进先出原则
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 1.创建队列
/* 参数解释
第一个参数:队列名称
第二个参数:队列属性(串行还是并发)
DISPATCH_QUEUE_SERIAL :串行
DISPATCH_QUEUE_CONCURRENT 并发
*/
// 1.1创建一个并发队列
// dispatch_queue_t queue = dispatch_queue_create("zj.queue", DISPATCH_QUEUE_CONCURRENT);
// 1.2系统内部已经给我们提供好了一个现成的并发队列
/*
第一个参数: iOS8以前是优先级, iOS8以后是服务质量
iOS8以前
* - DISPATCH_QUEUE_PRIORITY_HIGH 高优先级 2
* - DISPATCH_QUEUE_PRIORITY_DEFAULT: 默认的优先级 0
* - DISPATCH_QUEUE_PRIORITY_LOW: 低优先级 -2
* - DISPATCH_QUEUE_PRIORITY_BACKGROUND: 后台
iOS8以后
* - QOS_CLASS_USER_INTERACTIVE 0x21 用户交互(用户迫切想执行任务)
* - QOS_CLASS_USER_INITIATED 0x19 用户需要
* - QOS_CLASS_DEFAULT 0x15 默认
* - QOS_CLASS_UTILITY 0x11 工具(低优先级, 苹果推荐将耗时操作放到这种类型的队列中)
* - QOS_CLASS_BACKGROUND 0x09 后台
* - QOS_CLASS_UNSPECIFIED 0x00 没有设置
第二个参数: Flags that are reserved for future use. Always specify 0 for this parameter.
根据苹果文档,可以知道这是一个保留值,以便苹果在系统内部执行一些操作,并且苹果建议我们始终传入0即可.
*/
// 为了同时适配iOS8以及iOS8以前的版本,优先级参数直接传0即可
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.添加任务到队列(这里使用异步方式)
/* 参数解释
第一个参数:需要将任务添加到哪个队列
第二个参数:需要执行的任务
*/
// 连续添加三个任务
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
/* 结果:(每次打印的线程number顺序不一致)
<NSThread: 0x7fb878d02110>{number = 4, name = (null)}
<NSThread: 0x7fb878e21500>{number = 3, name = (null)}
<NSThread: 0x7fb878f91e20>{number = 2, name = (null)}
*/
}
2.GCD基本使用(异步函数+串行队列)
- 可以看到,开启了新的线程,但是只开启了一条
- 能够创建新线程的原因:使用”异步”函数调用
- 只创建1个子线程的原因:队列是串行队列
- 由于队列串行队列,所以任务是从上到下依次执行
- 由于使用了异步函数,那么不会等到异步函数中的任务执行完毕再去执行后面的代码
- (void)asynSerial{
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("zj.queue", DISPATCH_QUEUE_SERIAL);
// 2.添加任务到队列
// 连续添加三个任务
dispatch_async(queue, ^{
NSLog(@"任务一执行,%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务二执行,%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务三执行,%@",[NSThread currentThread]);
});
NSLog(@"+++++++");
/* 打印结果:
任务一执行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
+++++++
任务二执行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
任务三执行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
*/
}
3.GCD基本使用(同步函数+串行队列)
- 可以看到不会开启新的线程,且线程中的任务会依次执行
- 因为调用了同步函数, 那么会等同步函数中的任务执行完毕, 才会执行后面的代码
- (void)syncSerial
{
// 1.创建一个串行队列
// #define DISPATCH_QUEUE_SERIAL NULL
// 所以可以直接传NULL
dispatch_queue_t queue = dispatch_queue_create("zj.queue", NULL);
// 2.将任务添加到队列中
dispatch_sync(queue, ^{
NSLog(@"任务1 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3 == %@", [NSThread currentThread]);
});
NSLog(@"---------");
/*输出结果:
任务1 == <NSThread: 0x7fe129512690>{number = 1, name = main}
任务2 == <NSThread: 0x7fe129512690>{number = 1, name = main}
任务3 == <NSThread: 0x7fe129512690>{number = 1, name = main}
---------
*/
}
4.GCD基本使用(同步函数+并发队列)
- 使用了同步函数,不会创建新的线程
/*
同步 + 并发 : 不会开启新的线程
*/
- (void)syncConCurrent
{
// 1.创建一个并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.将任务添加到队列中
dispatch_sync(queue, ^{
NSLog(@"任务1 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3 == %@", [NSThread currentThread]);
});
NSLog(@"---------");
/*输出结果:
任务1 == <NSThread: 0x7fe129512690>{number = 1, name = main}
任务2 == <NSThread: 0x7fe129512690>{number = 1, name = main}
任务3 == <NSThread: 0x7fe129512690>{number = 1, name = main}
---------
*/
}
5.GCD基本使用(异步函数+主队列)
- 只要是在主队列,那么无论是调用同步还是异步函数,都不会开启新线程,一定会在主线程中执行
/*
异步 + 主队列 : 不会创建新的线程,并且任务是在主线程中执行
*/
- (void)asyncMain
{
// 创建主队列
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
}
/* 输出结果:
任务一 <NSThread: 0x7fa1d35267c0>{number = 1, name = main}
任务二 <NSThread: 0x7fa1d35267c0>{number = 1, name = main}
*/
6.GCD基本使用(同步函数+主队列)-死锁
- 如果是在主线程中调用同步函数 + 主队列, 那么会导致死锁
导致死锁的原因:
- sync函数是在主线程中执行的, 并且会等待block执行完毕. 先调用
- block是添加到主队列的, 也需要在主线程中执行. 后调用
- 由于队列的先进先出原则,那么sync函数要先执行,但是他又要等待block执行完毕才会往后继续执行,block又在主队列的后面,等待同步函数执行完毕后才会执行,所以两个任务相互等待,永远都不会往下继续执行,造成死锁
/*
在主线程中调用同步函数+主队列会导致死锁
*/
- (void)syncMain
{
NSLog(@"%@", [NSThread currentThread]);
// 主队列:
dispatch_queue_t queue = dispatch_get_main_queue();
// 如果是调用 同步函数, 那么会等同步函数中的任务执行完毕, 才会执行后面的代码
// 注意: 如果dispatch_sync方法是在主线程中调用的, 并且传入的队列是主队列, 那么会导致死锁
dispatch_sync(queue, ^{
NSLog(@"----------");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"----------");
}
/* 输出结果:队列中的任务以及主线程中后续的操作不再执行
<NSThread: 0x7fadbad1ba70>{number = 1, name = main}
*/
7.GCD基本使用(同步函数+主队列)-不死锁
- 要想使用同步函数和主队列,且不造成死锁,可以在子线程中执行同步函数
/*
如果是在子线程中调用 同步函数 + 主队列, 那么没有任何问题
*/
- (void)syncMain2
{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
// block会在子线程中执行
// NSLog(@"%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
// block一定会在主线程执行
NSLog(@"%@", [NSThread currentThread]);
});
});
NSLog(@"------------");
/* 输出结果:
------------
<NSThread: 0x7fd16af280d0>{number = 1, name = main}
*/
}