一、学点基础知识
(一)Queue队列:
Queue队列分为几下几种:
1、全局队列:
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2、主队列:
dispatch_get_main_queue()
3、用户创建的串行队列:
dispatch_queue_create(“com.yuna.com”, DISPATCH_QUEUE_SERIAL/NULL);
4、用户创建的并行队列:
dispatch_queue_create(“com.yuna.com”, DISPATCH_QUEUE_CONCURRENT);
(二)同步与异步
1. 同步
dispatch_sync(queue,block)
:同步队列,dispatch_sync函数不会立即返回,会阻塞当前线程,将block放到指定的queue上面执行,等待该block同步执行完成后才会返回
2. 异步
dispatch_async(queue,block)
:异步队列,dispatch_async函数会立即返回,block放到指定queue中等待执行,该block是并行还是串行只跟queue定义有关
(三)看看组合情况
/*
串行队列 + 同步任务:没有开启新线程,任务是逐个完成的
*/
- (void)serialSync {
dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_SERIAL);
NSLog(@"串行队列 + 同步----%@",[NSThread currentThread]);
// dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", NULL);
//在队列里添加同步任务
dispatch_sync(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"---1--%@", [NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"---2--%@", [NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"---3--%@", [NSThread currentThread]);
}
});
}
/*
串行队列 + 异步任务:开启新线程,任务是逐个完成的
*/
- (void)serialAsync {
dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", NULL);
NSLog(@"串行队列 + 异步----%@",[NSThread currentThread]);
//在队列里添加异步任务
dispatch_async(queue, ^{
for(int i = 0; i < 5;i++) {
NSLog(@"---1--%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for(int i = 0; i < 5; i++) {
NSLog(@"---2--%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for(int i = 0; i < 5; i++) {
NSLog(@"---3--%@", [NSThread currentThread]);
}
});
}
/*
并发队列 + 同步任务 :没有开启新线程,任务是逐个执行
*/
- (void)createConCurrentQueueSyn {
//创建并发队列
dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"并发队列 + 同步----%@",[NSThread currentThread]);
//在队列里添加同步任务
dispatch_sync(queue, ^{
for (int i = 0 ; i < 5;i++){
NSLog(@"---1--%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i=0 ; i < 5;i++) {
NSLog(@"---2--%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i=0 ; i < 5;i++){
NSLog(@"---3--%@",[NSThread currentThread]);
}
});
}
/*
并发队列 + 异步任务 :开启新线程,任务是并发的
*/
- (void)createConCurrentQueueAsyn {
dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"并发队列 + 异步----%@", [NSThread currentThread]);
//队列中添加异步任务
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--1--%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--2--%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--3--%@", [NSThread currentThread]);
}
});
}
/*
全局队列 + 同步任务:,没有开启新线程,任务逐个执行
*/
- (void)GlabolSyc {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"全局队列 + 同步----%@",[NSThread currentThread]);
//在队列里添加同步任务
dispatch_sync(queue, ^{
for (int i=0 ; i < 5; i++) {
NSLog(@"---1--%@", [NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i=0 ; i < 5; i++) {
NSLog(@"---2--%@", [NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i=0 ; i < 5; i++) {
NSLog(@"---3--%@", [NSThread currentThread]);
}
});
}
/*
全局 + 异步:开启新线程,任务是并发的
*/
- (void)GlobalAsync {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"全局队列 + 异步----%@", [NSThread currentThread]);
//队列中添加异步任务
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--1--%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--2--%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--3--%@", [NSThread currentThread]);
}
});
}
/*
主队列(串行)+同步:界面卡死
主队列中添加同步任务会造成死锁的
*/
//dispatch_sync 是指在指定线程队列queue 同步执行任务block,
//dispatch_sync具有等待block执行结束再能回调的特点;
- (void)mainthreadSync {
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"主队列(串行)+同步----%@",[NSThread currentThread]);
//在队列里添加同步任务
dispatch_sync(queue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"--1--%@", [NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"--2--%@", [NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 10; i++) {
NSLog(@"--3--%@", [NSThread currentThread]);
}
});
}
/*
主队列(串行)+异步:没有创建新线程,任务逐个完成 {都是在主线程中执行的}
*/
- (void)mainthreadAsync {
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"主队列(串行)+异步----%@",[NSThread currentThread]);
//在队列里添加异步任务
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--1--%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--2--%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"--3--%@", [NSThread currentThread]);
}
});
}
(四)添加依赖
我们可以从上面的内容看出,这些并发任务的顺序不能够很好地控制,如果我们想要让任务有一定的顺序该怎么办呢?比如任务4必须要在任务1完成后才能进行,这要如何控制呢?
这其实就是任务4依赖于任务1,也就是可以通过添加依赖来控制。
- (void)blockOperation {
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 封装操作
NSBlockOperation *blcp1 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务1
NSLog(@"任务1---%@", [NSThread currentThread]);
}];
NSBlockOperation *blcp2 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务2
NSLog(@"任务2---%@", [NSThread currentThread]);
}];
NSBlockOperation *blcp3 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务3
NSLog(@"任务3---%@", [NSThread currentThread]);
}];
NSBlockOperation *blcp4 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务4
NSLog(@"任务4---%@", [NSThread currentThread]);
}];
NSBlockOperation *blcp5 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务5
NSLog(@"任务5---%@", [NSThread currentThread]);
}];
// 添加操作依赖
[blcp3 addDependency:blcp1]; // blcp3依赖于blcp1
[blcp1 addDependency:blcp5]; // blcp1依赖于blcp5
[blcp5 addDependency:blcp2]; // blcp5依赖于blcp2
[blcp2 addDependency:blcp4]; // blcp2依赖于blcp4
// 将所有的任务添加到队列中
[queue addOperation:blcp1];
[queue addOperation:blcp2];
[queue addOperation:blcp3];
[queue addOperation:blcp4];
[queue addOperation:blcp5];
}
可以看到,这上面几个任务互相添加了依赖。推断一下,进行的顺序会是什么呢?
4->2->5->1->3
看看结果:
果然。
这上面是单个queue的依赖。如果我们在多个队列添加多个任务呢?
- (void)blockOperation2 {
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
// 封装操作
NSBlockOperation *blcp1 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务1
NSLog(@"任务1---%@", [NSThread currentThread]);
}];
NSBlockOperation *blcp2 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务2
NSLog(@"任务2---%@", [NSThread currentThread]);
}];
NSBlockOperation *blcp3 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务3
NSLog(@"任务3---%@", [NSThread currentThread]);
}];
NSBlockOperation *blcp4 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务4
NSLog(@"任务4---%@", [NSThread currentThread]);
}];
NSBlockOperation *blcp5 = [NSBlockOperation blockOperationWithBlock:^{
// 封装任务5
NSLog(@"任务5---%@", [NSThread currentThread]);
}];
// 添加操作依赖
[blcp3 addDependency:blcp1];
[blcp1 addDependency:blcp5];
[blcp5 addDependency:blcp2];
[blcp2 addDependency:blcp4];
// [blcp4 addDependency:blcp3]; // 循环依赖,运行程序以后无响应
// 将所有的任务添加到队列中
[queue addOperation:blcp1];
[queue2 addOperation:blcp2]; // 将blcp2添加到queue2中
[queue addOperation:blcp3];
[queue2 addOperation:blcp4]; // 将blcp4添加到queue2中
[queue addOperation:blcp5];
//在上面的代码中,我们新创建了一个队列queue2,
//并且将blcp2和blcp4添加到queue2中,
//其它的操作任然放在queue中,
//但是,程序运行以后,其结果依然和上面在一个队列中添加操作依赖时一样
}
发现还是一样的结果:
(五)其他
对NSNotificationCenter的分析
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(passValue:) name:@"PassValue" object:nil];
[self sentValue];
NSLog(@"通知赋值完毕");
}
- (void)sentValue {
NSLog(@"发送通知");
NSDictionary *dict = @{@"myValue":@"Billy通知传值"};
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:@"PassValue" object:nil userInfo:dict]];
}
- (void)passValue:(NSNotification *)text{
NSString *valueStr = text.userInfo[@"myValue"];
NSLog(@"收到值:%@",valueStr);
sleep(3);
}
分析:
通过打印的时间我们可以看出,当我们发送通知以后,观察者在接收到值以后,休眠了3秒,程序才会继续往下执行,也就是说这个过程是同步的;这里面设计为同步,大概是考虑到,一个通知可能有多个监听者,采用同步的方式能够保证所有的观察者都能够对通知做出相应,不会遗漏。
改为异步:
- (void)sentValue {
NSLog(@"发送通知");
NSDictionary *dict = @{@"myValue":@"Billy通知传值"};
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:@"PassValue" object:nil userInfo:dict]];
});
}
二、看看死锁
先理解几个概念:
(一)串行队列:
任务按照顺序添加在一个队列中,依次执行,常用的串行队列有主队列main queue和一般的串行队列serial queue。
(二)dispatch_sync():
同步任务,即前一个任务执行完毕,后面的任务才会开始执行,否则后面的任务都处于等待状态。
(三)死锁发生的原因:
在同一个串行队列中,如果主队列中的有个执行函数未执行完毕,恰好执行函数中通过dispatch_sync()
添加同步任务,这时就会发生死锁。主队列中添加同步任务会造成死锁。也就是:主队列中的执行函数当前是作为第一个任务正在执行,必须执行完该函数,才算执行完首个任务并返回结果,此时才会向下执行后面的任务,如果此时执行函数中又通过dispatch_sync()
同步添加了newTask,那么执行函数必须等刚添加的newTask执行完,才能向下执行,也就才算执行函数的调用结束,即首个任务执行完毕;而newTask作为同步添加的第二个任务,又在等待首个任务即执行函数执行完,才会开始执行,这样就出现了相互等待的情况,两个任务永远无法执行完,即出现死锁。
(四)例:
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"newTask");
});
同步对于任务是立刻执行的,那么当把任务放进主队列时,它就会立马执行,只有执行完这个任务,viewDidLoad
才会继续向下执行。而viewDidLoad
和任务都是在主队列上的,由于队列的先进先出原则,任务又需等待viewDidLoad
执行完毕后才能继续执行,viewDidLoad
和这个任务就形成了相互循环等待,就造成了死锁。想避免这种死锁,可以将同步改成异步dispatch_async
,或者将dispatch_get_main_queue
换成其他串行或并行队列,都可以解决。因此,不要用dispatch_sync()
把block压入当前队列。