明天要给师弟开分享会,分享GCD。 好方,只理解一些皮毛拿什么去装。准备的时候顺便把过程记录下来。安全
术语 | 含义 |
---|---|
进程 | 打开一个App就是开启一个进程。 |
线程 | 独立执行的代码段,一个线程同时间只能执行一个任务,反之多线程并发就能够在同一时间执行多个任务。在iOS系统中,一个进程包含一个主线程,它的主要任务是处理UI。其余线程称为子线程。 |
同步 | A执行完再执行B。 |
异步 | A和B能够同时执行。 |
任务 | 能够理解为某一堆要执行的代码。分为同步执行任务和异步执行任务。用block定义。 |
同步执行任务 | 按进入顺序执行的任务 |
异步执行任务 | 无论进入顺序,能够一块儿执行 |
队列 | 存听任务的结构。分为串行队列和并行队列。遵循先进先出。 |
队列组 | 将多线程进行分组,最大的好处是可获知全部线程的完成状况。 |
串行队列 | 线程执行只能依次逐一前后有序的执行。 |
并行队列 | 指两个或多个事件在同一时刻发生。多核CUP同时开启多条线程供多个任务同时执行,互不干扰。 |
并发 | 指两个或多个事件在同一时间间隔内发生。能够在某条线程和其余线程之间反复屡次进行上下文切换,看上去就好像一个CPU可以而且执行多个线程同样。实际上是伪异步。 |
回头看以为有必要在这简单说明多线程 多核 并发并行的区别 和 子线程和主线程的联系。bash
最近玩了个游戏叫《Inside》,戴着头盔就能操纵机器人,感受不管是玩法仍是游戏剧情都超适合类比线程。 用这个举个例子。 假如你是国王,拿到了一张藏宝图,但这个宝藏要到每个地点才能得知下一个地点的信息(电路中内存地址)。因而你就操纵机器人A去找,找到后带回来。机器人A的路线就是一条线程。 当机器人A还在路程上,你又获得一张藏宝图。你这时候派机器人B去找,找到带回来。这时候机器人B的路线就是另外一条线程。 以上就是多线程。 这时候,只要你周期足够短,轮流戴头盔a和头盔b,,看上去就像你同时在操纵机器人A和机器人B。这就叫作并发!装出来的。 某一天,你的头快摇傻了。因而乎你长出了第二个头。(对应着双核CPU),这时候就是名副其实地同时操纵。这就叫并行,必需要多头怪才拥有这技能。 但若是又操纵第三个机器人,这时候只能再来回戴了,又要并发了。 A找到并回到了城堡把结果带回给你,你才发现你也是个机器人(主线程)。其余机器人带回宝藏后就能够拜拜了,但就算还有没有宝藏在路上,你都不能拜拜,必须保持呼吸(runloop)。 这就是子线程和主线程的联系。 子线程的任务所有完成后,最终会回到主线程。主线程中运行着runloop网络
就是把任务加到队列中 队列能够本身新建。 系统也有 全局并发队列和主队列。多线程
#pragma mark - 建立队列
// 建立队列
// 第一个参数 队列名称
// 第二个参数的做用:串行(DISPATCH_QUEUE_SERIAL)、并行(DISPATCH_QUEUE_CONCURRENT)。
dispatch_queue_t queue = dispatch_queue_create("net.Hsusue.testQueue", DISPATCH_QUEUE_CONCURRENT);
* 经常使用的系统并发队列——全局并发队列
//程序默认的队列级别,通常不要修改,DISPATCH_QUEUE_PRIORITY_DEFAULT == 0
dispatch_queue_t globalQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//HIGH
dispatch_queue_t globalQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//LOW
dispatch_queue_t globalQueue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//BACKGROUND
dispatch_queue_t globalQueue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 获取主队列有特别函数(是个串行队列)
// dispatch_queue_t queue = dispatch_get_main_queue();
#pragma mark - 建立任务加到队列中
// 同步执行任务建立方法
dispatch_sync(queue, ^{
// 这里放同步执行任务代码
});
// 异步执行任务建立方法
dispatch_async(queue, ^{
// 这里放异步执行任务代码
});
复制代码
仍是不能忘了《Inside》的例子。并发
两种待办任务表(对应队列) 一种是多个机器人对多个宝藏,先入先出发。(对应并行队列) 另外一种是一个机器人对有顺序找的多个宝藏。(对应串行队列) 特殊的 强行本身去作的任务表。 (对应主队列)app
你有两类事情(对应任务) 一类是吃喝拉撒,一有须要就本身立刻去作,总不能懒到让机器人帮忙吧。(对应着同步执行任务) 另外一类是寻宝,要机器人去作,出发前要点时间给机器人充电。(对应着异步执行任务)异步
代码中, 输出@"1"对应着吃喝拉撒
async
- (void)viewDidLoad {
[super viewDidLoad];
[self asyncConcurrent];
NSLog(@"1");
}
//异步执行 + 并行队列
- (void)asyncConcurrent{
//建立一个并行队列
dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---start---");
//使用异步函数封装三个任务
dispatch_async(queue, ^{
NSLog(@"任务A---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务B---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
复制代码
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
[self asyncSerial];
NSLog(@"1---%@", [NSThread currentThread]);
}
//异步 + 串行队列
- (void)asyncSerial{
//建立一个串行队列
dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_SERIAL);
NSLog(@"---start---");
//使用异步函数封装三个任务
dispatch_async(queue, ^{
NSLog(@"任务A---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务B---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
复制代码
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
[self syncConcurrent];
NSLog(@"1---%@", [NSThread currentThread]);
}
//同步 + 并行队列
- (void)syncConcurrent{
//建立一个并行队列
dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---start---");
//使用同步函数封装三个任务
dispatch_sync(queue, ^{
NSLog(@"任务A---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务B---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
复制代码
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
[self syncSerial];
NSLog(@"1---%@", [NSThread currentThread]);
}
//同步 + 串行队列
- (void)syncSerial{
//建立一个串行队列
dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_SERIAL);
NSLog(@"---start---");
//使用异步函数封装三个任务
dispatch_sync(queue, ^{
NSLog(@"任务A---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务B---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
复制代码
一要吃喝拉撒就本身立刻去作。因此不等@“end”输出就先作完了。最后再@“1”。有着必然前后顺序。ide
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
// [self syncSerial];
[self asyncMain];
NSLog(@"1---%@", [NSThread currentThread]);
}
//异步 + 主队列
- (void)asyncMain{
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"---start---");
//使用异步函数封装三个任务
dispatch_async(queue, ^{
NSLog(@"任务A---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务B---%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
复制代码
和异步 + 串行队列区别就是不开启新线程。 函数
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
// [self syncSerial];
// [self asyncMain];
[self syncMain];
NSLog(@"1---%@", [NSThread currentThread]);
}
//同步+主队列(死锁)
- (void)syncMain{
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"---start---");
//使用同步函数封装三个任务
dispatch_sync(queue, ^{
NSLog(@"任务A---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务B---%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务C---%@", [NSThread currentThread]);
});
NSLog(@"---end---");
}
复制代码
计算机指令包括操做码和地址码。
每一个函数进入都会记住进入的地址码,return时就会回去。
上面主队列在主队列中加了任务。 实质在同一个同步串行队列中,再使用该串行队列同步的执行任务。
[self syncMain]这是主队列作(出)的事(同步且未作完)。根据先进先出,主队列头是syncMain。而后假设这里的内存地址是1。
dispatch_sync(queue, ^{
NSLog(@"任务A---%@", [NSThread currentThread]);
});
// 假设运行时此处内存地址为1
复制代码
添加了一个block到主队列尾部,要等主队列头synMain执行完才能执行。 原本应该执行追加任务B,可是电路上的地址并无回来,由于dispatch_sync
要执行完block才reutrn。 由于被代码被黑盒子包起来了,大胆猜想一下。 假设内存地址为2
// 调用时记住进入地址为1
dispatch_sync {
// block执行完才return
// 运行时此处内存地址为2
if( block() ) { // block执行完
return;//返回到进入地址
}
}
复制代码
因而代码能够当作 卡在了该函数内部,内存地址为2处。 没有回到1处,天然就不会追加任务B。
上面说了不少种方法,禁止死锁状况开发中是很容易记住的。 但其余组合,即便想的时候能想懂,但也仍是很混乱。 根据我我的经验,平常开发中先从宏观上想是否须要耗时(耗时放到子线程),是否有序。 一般是须要和主线程同时执行(开新线程,即异步执行任务)才会用到GCD。 多是开发经验不够。
从子线程,异步返回主线程更新UI。 队列经常使用全局并行队列。 由于要下载图片耗时,并且具备网络不稳定性,因此放到子线程。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3948453733,2367168123&fm=27&gp=0.jpg"]];
UIImage *image = [UIImage imageWithData:imgData];
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
UIImageView *imgView = [[UIImageView alloc]initWithImage:image];
[imgView setFrame:CGRectMake(0, 0, 200, 200)];
[imgView setCenter:self.view.center];
[self.view addSubview:imgView];
});
});
复制代码
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
// [self syncSerial];
// [self asyncMain];
// [self syncMain];
[self groupTest];
NSLog(@"1---%@", [NSThread currentThread]);
}
- (void)groupTest {
dispatch_queue_t conCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_group_t groupQueue = dispatch_group_create();
NSLog(@"current task");
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
NSLog(@"并行任务1");
});
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
NSLog(@"并行任务2");
});
dispatch_group_notify(groupQueue, mainQueue, ^{
NSLog(@"groupQueue中的任务 都执行完成,回到主线程更新UI");
});
NSLog(@"next task");
}
复制代码
dispatch_group_t groupQueue = dispatch_group_create();
用于生成队列组 2.生成队列时加上前缀_guoup 3.
dispatch_group_notify
这个函数用以处理其余队列完成的块。
static dispatch_once_ onceToken;
dispatch_once( &onceToken,^{
对象A =[ [对象A alloc] init];
});
复制代码
- (void)viewDidLoad {
[super viewDidLoad];
// [self asyncConcurrent];
// [self asyncSerial];
// [self syncConcurrent]
// [self syncSerial];
// [self asyncMain];
// [self syncMain];
// [self groupTest];
[self barrier];
NSLog(@"1---%@", [NSThread currentThread]);
}
// 栏栅函数
- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任务A---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务B---%@",[NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"栏栅函数---%@",[NSThread currentThread]);
});
// 换成同步执行也同样
// dispatch_barrier_sync(queue, ^{
// NSLog(@"栏栅函数---%@",[NSThread currentThread]);
// });
dispatch_async(queue, ^{
NSLog(@"任务C---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务D---%@",[NSThread currentThread]);
});
}
复制代码
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t i) {
NSLog(@"%zd----%@", i, [NSThread currentThread];
}
复制代码
加锁方法
方法一 互斥锁(同步锁)@synchronized(锁对象) {
// 须要锁定的代码
}
复制代码
判断的时候锁对象要存在,若是代码中只有一个地方须要加锁,大多都使用self做为锁对象,这样能够避免单独再建立一个锁对象。 方法二:自旋锁 用到属性修饰原子属性nonatomic
和 atomic非原子属性
力荐第三篇,看了不少瞎说的,就这篇真实!!!