同步执行和异步执行,这大概是史上最详细、清

dispatch_sync(dispatch_queue_tqueue, dispatch_block_tblock);

NSLog(@"----2--%@", [NSThreadcurrentThread]);

GCD的使用步骤

1、创建一个队列(串行队列或并发队列)

2、将任务添加到队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)

2018-02-23 22:05:05.792725+0800 YSC-GCD-demo[20494:5183656] 2---{number = 3, name = (null)}

八、调度组(dispatch_group)

图片 1#pragma mark #pragma mark - 调度组 /** 调度组的实现原理:类似引用计数器进行+1和-1的操作 应用场景 比如同时开了三个线程下载视频,只有当三个视频完全下载完毕后,我才能做后续的事 这个就需要用到调度组,这个调度组,就能监听它里面的任务是否都执行完毕 */

  • (void)groupDispatch { // 1.创建调度组 dispatch_group_t group = dispatch_group_create(); // 2.获取全局队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 3.创建三个下载任务 void (^task1) () = ^(){ NSLog(@"%@----下载片头",[NSThread currentThread]); }; dispatch_group_enter(group); // 引用计数+1 void (^task2) () = ^(){ NSLog(@"%@----下载中间的内容",[NSThread currentThread]); [NSThread sleepForTimeInterval:3.0]; NSLog(@"--下载中间内容完毕---"); dispatch_group_leave(group); // 引用计数-1 }; dispatch_group_enter(group); // 引用计数+1 void (^task3) () = ^(){ NSLog(@"%@----下载片尾",[NSThread currentThread]); dispatch_group_leave(group); // 引用计数-1 }; // 4.需要将我们的队列 和 任务,加入到组内去监控 dispatch_group_async(group, queue, task1); dispatch_group_async(group, queue, task2); dispatch_group_async(group, queue, task3); // 5.监听的函数 /** 远离:来监听当调度组的引用计数器为0时,才会执行该函数中内容,否则不会执行 参数1:组 参数2:决定了参数3在哪个线程里面执行 参数3:组内完全下载完毕后需要执行的代码 */ dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 表示组内的所有内容全部下载完成后会来到这里 NSLog(@"把下好的视频按照顺序拼接好,然后显示在UI去播放%@", [NSThread currentThread]); }); } 调度组

 

1.因为你在使用的是同步的 dispatch_group_wait ,它会阻塞当前线程,所以你要用 dispatch_async 将整个方法放入后台队列以避免阻塞主线程。

2.创建一个新的 Dispatch Group,它的作用就像一个用于未完成任务的计数器。
3.dispatch_group_enter 手动通知 Dispatch Group 任务已经开始。你必须保证 dispatch_group_enter 和 dispatch_group_leave 成对出现,否则你可能会遇到诡异的崩溃问题。
4.手动通知 Group 它的工作已经完成。再次说明,你必须要确保进入 Group 的次数和离开 Group 的次数相等。
5.dispatch_group_wait 会一直等待,直到任务全部完成或者超时。如果在所有任务完成前超时了,该函数会返回一个非零值。你可以对此返回值做条件判断以确定是否超出等待周期;然而,你在这里用 DISPATCH_TIME_FOREVER 让它永远等待。它的意思,勿庸置疑就是,永-远-等-待!这样很好,因为图片的创建工作总是会完成的。
6.此时此刻,你已经确保了,要么所有的图片任务都已完成,要么发生了超时。然后,你在主线程上运行 completionBlock 回调。这会将工作放到主线程上,并在稍后执行。
7.最后,检查 completionBlock 是否为 nil,如果不是,那就运行它。
编译并运行你的应用,尝试下载多个图片,观察你的应用是在何时运行 completionBlock 的。

注意:如果你是在真机上运行应用,而且网络活动发生得太快以致难以观察 completionBlock 被调用的时刻,那么你可以在 Settings 应用里的开发者相关部分里打开一些网络设置,以确保代码按照我们所期望的那样工作。只需去往 Network Link Conditioner 区,开启它,再选择一个 Profile,“Very Bad Network” 就不错。
如果你是在模拟器里运行应用,你可以使用 来自 GitHub 的 Network Link Conditioner 来改变网络速度。它会成为你工具箱中的一个好工具,因为它强制你研究你的应用在连接速度并非最佳的情况下会变成什么样。

 


NSLog(@"1---%@",[NSThreadcurrentThread]);

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

1. 队列的创建方法

可以使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并发队列。

// 串行队列的创建方法

dispatch_queue_tqueue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);

// 并发队列的创建方法

dispatch_queue_tqueue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

对于并发队列,还可以使用dispatch_get_global_queue来创建全局并发队列。GCD默认提供了全局的并发队列,需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。

/**

四、定时源事件和子线程的运行循环

 1 - (void)viewDidLoad {
 2     [super viewDidLoad];
 3     
 4     NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES];
 5     
 6     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
 7     
 8 }
 9 
10 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
11     [self performSelectorInBackground:@selector(subThreadRun) withObject:nil];
12 }
13 
14 #pragma mark
15 #pragma mark - 子线程的运行循环
16 - (void)subThreadRun {
17     
18     NSLog(@"%@----%s", [NSThread currentThread], __func__);
19     
20     // 1.定义一个定时器
21     NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES];
22     
23     // 2.将我们的定时器加入到运行循环,只有加入到当前的运行循环里面去,他才知道你这个时候,有一个定时任务
24     /**
25      NSDefaultRunLoopMode 当拖动的时候,它会停掉
26      因为这种模式是互斥的
27      forMode:UITrackingRunLoopMode 只有输入的时候,它才会去执行定时器任务
28      
29      NSRunLoopCommonModes 包含了前面两种
30      
31     //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
32     //[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
33      */
34     [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
35     
36     // 下载、定时源时间、输入源时间,如果放在子线程里面,如果想要它执行任务,就必须开启子线程的运行循环
37     CFRunLoopRun();
38     
39 }
40 
41 - (void)timeEvent {
42     
43     NSLog(@"%d----%@", self.count, [NSThread currentThread]);
44     
45     if (self.count++ == 10) {
46         NSLog(@"---挂了----");
47         // 停止当前的运行循环
48         CFRunLoopStop(CFRunLoopGetCurrent());
49     }
50     
51 }

  

温馨提示:在完成本篇Blog的过程中, Selander。

 

本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解: 一、什么是GCD 二、我们为什么要用...

dispatch_group_t group = dispatch_group_create();

队列:用来存放任务

GCD核心概念:任务和队列

任务:在线程中执行的那段代码。执行方式有两种:同步执行和异步执行

* 同步执行:只能在当前线程执行,不具有开启新线程的能力(dispatch_sync)

* 异步执行:可以在新线程中执行任务,具有开启新线程的能力(dispatch_async)

队列:这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列并发队列

* 并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)

          1)并发功能只有在异步(dispatch_async)函数下才有效

* 串行队列(Serial Dispatch Queue):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

可以开启多个线程,任务交替(同时)执行。

一、Synchronous & Asynchronous 同步 & 异步

1)同步任务执行方式:在当前线程中执行,必须等待当前语句执行完毕,才会执行下一条语句

图片 2

 

图片 3#pragma mark #pragma mark - 同步方法 /** 同步的打印顺序 打印 begin 打印 [NSThread currentThread] 打印 end */ - (void)syncTask { NSLog(@"begin"); // 1.GCD同步方法 /** 参数1:队列 第一个参数0其实为队列优先级DISPATCH_QUEUE_PRIORITY_DEFAULT,如果要适配 iOS 7.0 & 8.0,则始终为0 参数2:任务 */ dispatch_sync(dispatch_get_global_queue(0, 0), ^{ // 任务中要执行的代码 NSLog(@"%@", [NSThread currentThread]); }); NSLog(@"end"); } 同步方法

 

2)异步任务执行方式:不在当前线程中执行,不用等待当前语句执行完毕,就可以执行下一条语句

图片 4

 

图片 5#pragma mark #pragma mark - 异步方法 /** 异步的打印顺序 打印 begin 打印 一般情况下为end,极少数情况下会很快开辟完新的线程,先打印出[NSThread currentThread] */ - (void)asyncTask { /** 异步:不会在“当前线程”执行,会首先去开辟新的子线程,开辟线程需要花费时间 */ NSLog(@"begin"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"%@", [NSThread currentThread]); }); NSLog(@"end"); } 异步方法

 

从子线程回到主线程

});

GCD基本使用

1. 并发队列 + 同步执行

- (void) syncConcurrent{

NSLog(@"syncConcurrent---begin");

dispatch_queue_tqueue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_sync(queue, ^{for(inti =0; i <2; ++i) {

        NSLog(@"1------%@",[NSThreadcurrentThread]); 

}    });

dispatch_sync(queue, ^{for(inti =0; i <2; ++i) {

        NSLog(@"2------%@",[NSThreadcurrentThread]);       

}    });

dispatch_sync(queue, ^{for(inti =0; i <2; ++i) {

        NSLog(@"3------%@",[NSThreadcurrentThread]);       

}    });

NSLog(@"syncConcurrent---end");}

2. GCD的延时执行方法dispatch_after

当我们需要延迟执行一段代码时,就需要用到GCD的dispatch_after方法。

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{   

      // 2秒后异步执行这里的代码...  NSLog(@"run-----");

});

3. GCD的一次性代码(只执行一次)dispatch_once

我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了GCD的dispatch_once方法。使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次。

staticdispatch_once_tonceToken;

dispatch_once(&onceToken, ^{

         // 只执行1次的代码(这里面默认是线程安全的)

});

4. GCD的快速迭代方法dispatch_apply

通常我们会用for循环遍历,但是GCD给我们提供了快速迭代的方法dispatch_apply,使我们可以同时遍历。比如说遍历0~5这6个数字,for循环的做法是每次取出一个元素,逐个遍历。dispatch_apply可以同时遍历多个数字。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_apply(6, queue, ^(size_tindex) {   

         NSLog(@"%zd------%@",index, [NSThread currentThread]);

});

5. GCD的队列组dispatch_group

有时候我们会有这样的需求:分别异步执行2个耗时操作,然后当2个耗时操作都执行完毕后再回到主线程执行操作。这时候我们可以用到GCD的队列组。

我们可以先把任务放到队列中,然后将队列放入队列组中。

调用队列组的dispatch_group_notify回到主线程执行操作。

dispatch_group_t group =  dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{    

      // 执行1个耗时的异步操作});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{    

      // 执行1个耗时的异步操作

});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

      // 等前面的异步操作都执行完毕后,回到主线程...

});

2018-02-23 20:46:01.995589+0800 YSC-GCD-demo[20111:5046708] 3---{number = 1, name = main}

五、同步的作用

同步任务,可以让其他异步执行的任务,依赖某一个同步任务,例如:在用户登录之后,才允许异步下载文件!

图片 6#pragma mark #pragma mark - 模拟登录下载多个电影数据 /** 同步的作用:保证我们任务执行的先后顺序 1.登录 2.同时下载三部电影 */ - (void)loadManyMovie { dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"%@", [NSThread currentThread]); // 1.登录,同步在当前线程里面工作 dispatch_sync(dispatch_get_global_queue(0, 0), ^{ NSLog(@"登录了---%@", [NSThread currentThread]); sleep(3); }); // 2.同时下载三部电影() dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"正在下载第一个电影---%@", [NSThread currentThread]); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"正在下载第二个电影---%@", [NSThread currentThread]); }); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"正在下载第三个电影---%@", [NSThread currentThread]); }); dispatch_sync(dispatch_get_main_queue(), ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"计算机将在三秒后关闭---%@", [NSThread currentThread]); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"关机了---%@", [NSThread currentThread]); }); }); }); } 模拟登录下载多个电影数据

 

任务:执行什么操作

});

2. 任务的创建方法

// 同步执行任务创建方法

dispatch_sync(queue, ^{

          NSLog(@"%@",[NSThreadcurrentThread]);

          // 这里放任务代码

});

// 异步执行任务创建方法

dispatch_async(queue, ^{

           NSLog(@"%@",[NSThreadcurrentThread]);

            // 这里放任务代码

});

所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中)。

二、我们为什么要用GCD技术

  • GCD 能通过推迟昂贵计算任务并在后台运行它们来改善你的应用的响应性能。
  • GCD 提供一个易于使用的并发模型而不仅仅只是锁和线程,以帮助我们避开并发陷阱。
  • GCD 具有在常见模式(例如单例)上用更高性能的原语优化你的代码的潜在能力。
  • GCD旨在替换NSThread等线程技术
  • GCD可充分利用设备的多核
  • GCD可自动管理线程的生命周期

 


并发和串行主要影响:任务的执行方式

注意:

2018-02-23 20:44:25.387687+0800 YSC-GCD-demo[20083:5040132] 2---{number = 1, name = main}

三、在实际开发中如何使用GCD更好的实现我们的需求

同步函数

b.串行队列(Serial Dispatch Queue):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

从dispatch_group_enter、dispatch_group_leave相关代码运行结果中可以看出:当所有任务执行完成之后,才执行 dispatch_group_notify 中的任务。这里的dispatch_group_enter、dispatch_group_leave组合,其实等同于dispatch_group_async。

六、dispatch_time延迟操作

不知道何时适合使用 dispatch_after ?

  • 自定义串行队列:在一个自定义串行队列上使用 dispatch_after 要小心。你最好坚持使用主队列。
  • 主队列(串行):是使用 dispatch_after 的好选择;Xcode 提供了一个不错的自动完成模版。
  • 并发队列:在并发队列上使用 dispatch_after 也要小心;你会这样做就比较罕见。还是在主队列做这些操作吧。

图片 7// MARK:

  • 延迟执行 - (void)delay { /** 从现在开始,经过多少纳秒,由"队列"调度异步执行 block 中的代码 参数 1. when 从现在开始,经过多少纳秒 2. queue 队列 3. block 异步执行的任务 */ dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)); void (^task)() = ^ { NSLog(@"%@", [NSThread currentThread]); }; // 主队列 // dispatch_after(when, dispatch_get_main_queue(), task); // 全局队列 // dispatch_after(when, dispatch_get_global_queue(0, 0), task); // 串行队列 dispatch_after(when, dispatch_queue_create("itheima", NULL), task); NSLog(@"come here"); } - (void)after { [self.view performSelector:@selector(setBackgroundColor:) withObject:[UIColor orangeColor] afterDelay:1.0]; NSLog(@"come here"); } 延迟执行

 

dispatch_queue_t dispatch_get_global_queue(

staticdispatch_once_tonceToken;

* 同步执行 + 串行队列

七、线程安全(单例dispatch_once、读写dispatch_barrier_async)

  一个常见的担忧是它们常常不是线程安全的。这个担忧十分合理,基于它们的用途:单例常常被多个控制器同时访问。

  单例的线程担忧范围从初始化开始,到信息的读和写。

dispatch_once() 以线程安全的方式执行且仅执行其代码块一次。试图访问临界区(即传递给 dispatch_once 的代码)的不同的线程会在临界区已有一个线程的情况下被阻塞,直到临界区完成为止。

图片 8// 使用 dispatch_once 实现单例 + (instancetype)sharedSingleton { static id instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); return instance; } 使用 dispatch_once 实现单例

  线程安全实例不是处理单例时的唯一问题。如果单例属性表示一个可变对象,那么你就需要考虑是否那个对象自身线程安全。

  如果问题中的这个对象是一个 Foundation 容器类,那么答案是——“很可能不安全”!Apple 维护一个有用且有些心寒的列表,众多的 Foundation 类都不是线程安全的。如:NSMutableArray。

  虽然许多线程可以同时读取 NSMutableArray 的一个实例而不会产生问题,但当一个线程正在读取时让另外一个线程修改数组就是不安全的。在目前的状况下不能预防这种情况的发生。GCD 通过用 dispatch barriers 创建一个读者写者锁,提供了一个优雅的解决方案。

 

各种队列的执行效果

a.用同步的方式执行任务

*/- (void)barrier {dispatch_queue_tqueue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue, ^{// 追加任务1for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程}    });dispatch_async(queue, ^{// 追加任务2for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程}    });        dispatch_barrier_async(queue, ^{// 追加任务 barrierfor(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"barrier---%@",[NSThreadcurrentThread]);// 打印当前线程}    });dispatch_async(queue, ^{// 追加任务3for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"3---%@",[NSThreadcurrentThread]);// 打印当前线程}    });dispatch_async(queue, ^{// 追加任务4for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"4---%@",[NSThreadcurrentThread]);// 打印当前线程}    });}

三、全局队列

全局队列是系统为了方便程序员开发提供的,其工作表现与并发队列一致

全局队列 & 并发队列的区别

  全局队列:没有名称,无论 MRC & ARC 都不需要考虑释放,日常开发中,建议使用"全局队列"
  并发队列:有名字,和 NSThread 的 name 属性作用类似,如果在 MRC 开发时,需要使用 dispatch_release(q); 释放相应的对象
  dispatch_barrier 必须使用自定义的并发队列
  开发第三方框架时,建议使用并发队列

参数
  参数1:服务质量(队列对任务调度的优先级)/iOS 7.0 之前,是优先级

iOS 8.0(新增,暂时不能用,今年年底)
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, 未指定,可以和iOS 7.0 适配
iOS 7.0
DISPATCH_QUEUE_PRIORITY_HIGH 2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW (-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级

  参数2:为未来保留使用的,应该永远传入0

  结论:如果要适配 iOS 7.0 & 8.0,使用以下代码: dispatch_get_global_queue(0, 0);

 

图片 9#pragma mark #pragma mark - 全局队列同步任务 /** 全局队列,同步任务 1.打印顺序:依次执行,因为它是同步的 2.在哪条线程上执行:主线程,因为它是同步方法,它就在当前线程里面执行 当它遇到同步的时候,并发队列还是依次执行,所以说,方法的优先级比队列的优先级高 * 只要是同步方法,都只会在当前线程里面执行,不会开子线程 应用场景:开发中几乎不用 */ - (void)globalSync { /** 参数1: IOS7:表示的优先级 IOS8:服务质量 为了保证兼容IOS7&IOS8一般传入0 参数2:未来使用,传入0 */ NSLog(@"begin"); // 1.创建全局队列 dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); // 2.创建任务 void (^task1)() = ^() { NSLog(@"task1----%@", [NSThread currentThread]); }; void (^task2)() = ^() { NSLog(@"task2----%@", [NSThread currentThread]); }; void (^task3)() = ^() { NSLog(@"task3----%@", [NSThread currentThread]); }; // 3.添加任务到全局队列 dispatch_sync(globalQueue, task1); dispatch_sync(globalQueue, task2); dispatch_sync(globalQueue, task3); NSLog(@"end"); } 全局队列同步任务 图片 10#pragma mark #pragma mark - 全局队列异步任务 /** 全局队列,异步方法 1.打印顺序:无序的 2.在子线程上执行,每一个任务都在它自己的线程上执行,线程数由底层可调度线程池来决定的,可调度线程池有一个重用机制 应用场景: 蜻蜓FM同时下载多个声音 */ - (void)globalAsync { NSLog(@"begin"); dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0); void (^task1)() = ^() { NSLog(@"task1---%@", [NSThread currentThread]); }; void (^task2)() = ^() { NSLog(@"task2---%@", [NSThread currentThread]); }; void (^task3)() = ^() { NSLog(@"task3---%@", [NSThread currentThread]); }; dispatch_async(globalQueue, task1); dispatch_async(globalQueue, task2); dispatch_async(globalQueue, task3); NSLog(@"end"); } 全局队列异步任务

 

dispatch_apply(subpaths.count,queue, ^(size_t index) {

dispatch_queue_tqueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

* 队列组 dispatch_group_wait

四、主队列

特点
  专门用来在主线程上调度任务的队列
  不会开启线程
  以先进先出的方式,在主线程空闲时才会调度队列中的任务在主线程执行
  如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度

队列获取
  主队列是负责在主线程调度任务的
  会随着程序启动一起创建
  主队列只需要获取不用创建

 

图片 11#pragma mark #pragma mark - 主队列异步任务 /** 主队列,异步任务 1.执行顺序:依次执行,因为它在主线程里面执行 * 似乎与我们的异步任务有所冲突,但是因为它是主队列,所以,只在主线程里面执行 2.是否会开线程:不会,因为它在主线程里面执行 应用场景: 当做了耗时操作之后,我们需要回到主线程更新UI的时候,就非它不可 */ - (void)mainAsync { NSLog(@"begin"); // 1.创建主队列 dispatch_queue_t mainAsync = dispatch_get_main_queue(); // 2.创建任务 void (^task1)() = ^() { NSLog(@"task1---%@", [NSThread currentThread]); }; void (^task2)() = ^() { NSLog(@"task2---%@", [NSThread currentThread]); }; void (^task3)() = ^() { NSLog(@"task3---%@", [NSThread currentThread]); }; dispatch_async(mainAsync, task1); dispatch_async(mainAsync, task2); dispatch_async(mainAsync, task3); NSLog(@"end"); } 主队列异步任务 图片 12#pragma mark #pragma mark - 主队列同步方法有问题,不能用是个奇葩,会造成死锁 /** 主队列,同步任务有问题,不能用,彼此都在等对方是否执行完了,所以是互相死等 主队列只有在主线程空闲的时候,才会去调度它里面的任务去执行 */ - (void)mainSync { NSLog(@"begin"); // 1.创建主队列 dispatch_queue_t mainSync = dispatch_get_main_queue(); // 2.创建任务 void (^task1)() = ^() { NSLog(@"task1---%@", [NSThread currentThread]); }; void (^task2)() = ^() { NSLog(@"task2---%@", [NSThread currentThread]); }; void (^task3)() = ^() { NSLog(@"task3---%@", [NSThread currentThread]); }; // 3.添加任务到主队列中 dispatch_sync(mainSync, task1); dispatch_sync(mainSync, task2); dispatch_sync(mainSync, task3); NSLog(@"end"); } 主队列同步方法有问题,不能用是个奇葩,会造成死锁

 

Deadlock 死锁

  两个(有时更多)东西——在大多数情况下,是线程——所谓的死锁是指它们都卡住了,并等待对方完成或执行其它操作。第一个不能完成是因为它在等待第二个的完成。但第二个也不能完成,因为它在等待第一个的完成。

 

});

b.用异步的方式执行任务

* 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。

二、Serial Queues & Concurrent Queues 串行 & 并发

1)串行队列调度同步和异步任务执行

串行队列特点:
  以先进先出的方式,顺序调度队列中的任务执行
  无论队列中所指定的执行任务函数是同步还是异步,都会等待前一个任务执行完成后,再调度后面的任务

图片 13

 

图片 14#pragma mark #pragma mark - 串行队列同步方法 /** 串行队列,同步方法 1.打印顺序 : 从上到下,依次打印,因为是串行的 2.在哪条线程上执行 : 主线程,因为是同步方法,所以在当前线程里面执行,恰好当前线程是主线程,所以它就在主线程上面执行 应用场景:开发中很少用 */ - (void)serialSync { // 1.创建一个串行队列 /** 参数1:队列的表示符号,一般是公司的域名倒写 参数2:队列的类型 DISPATCH_QUEUE_SERIAL 串行队列 DISPATCH_QUEUE_CONCURRENT 并发队列 */ dispatch_queue_t serialQuene = dispatch_queue_create("com.baidu", DISPATCH_QUEUE_SERIAL); // 创建任务 void (^task1) () = ^() { NSLog(@"task1---%@", [NSThread currentThread]); }; void (^task2) () = ^() { NSLog(@"task2---%@", [NSThread currentThread]); }; void (^task3) () = ^() { NSLog(@"task3---%@", [NSThread currentThread]); }; // 添加任务到队列,同步方法执行 dispatch_sync(serialQuene, task1); dispatch_sync(serialQuene, task2); dispatch_sync(serialQuene, task3); } 串行队列同步方法

 

图片 15#pragma mark #pragma mark - 串行队列异步方法 /** 串行队列,异步方法 1.打印顺序:从上到下,依次执行,它是串行队列 2.在哪条线程上执行:在子线程,因为它是异步执行,异步就是不在当前线程里面执行 应用场景:耗时间,有顺序的任务 1.登录--->2.付费--->3.才能看 */ - (void)serialAsync { // 除了第三步,和串行同步方法中都是一样的 // 1.创建一个串行队列 dispatch_queue_t serialQuene = dispatch_queue_create("com.baidu", DISPATCH_QUEUE_SERIAL); // 2.创建任务 void (^task1)() = ^() { NSLog(@"task1---%@", [NSThread currentThread]); }; void (^task2)() = ^() { NSLog(@"task2---%@", [NSThread currentThread]); }; void (^task3)() = ^() { NSLog(@"task3---%@", [NSThread currentThread]); }; // 3.添加任务到队列 dispatch_async(serialQuene, task1); dispatch_async(serialQuene, task2); dispatch_async(serialQuene, task3); } 串行队列异步方法

 

2)并发队列调度异步任务执行

并发队列特点:
  以先进先出的方式,并发调度队列中的任务执行
  如果当前调度的任务是同步执行的,会等待任务执行完成后,再调度后续的任务
  如果当前调度的任务是异步执行的,同时底层线程池有可用的线程资源,会再新的线程调度后续任务的执行

图片 16

 

图片 17#pragma mark #pragma mark - 并发队列同步任务 /** 并发队列,同步任务 1.打印顺序:因为是同步,所以依次执行 2.在哪条线程上执行:主线程,因为它是同步方法,它就在当前线程里面执行,也就是在主线程里面依次执行 当并发队列遇到同步的时候还是依次执行,所以说方法(同步/异步)的优先级会比队列的优先级高 * 只要是同步方法,都只会在当前线程里面执行,不会开子线程 应用场景: 开发中几乎不用 */ - (void)serialSync { /** 参数1:队列的表示符号,一般是公司的域名倒写 参数2:队列的类型 DISPATCH_QUEUE_SERIAL 串行队列 DISPATCH_QUEUE_CONCURRENT 并发队列 */ // 1.创建并发队列 dispatch_queue_t serialSync = dispatch_queue_create("com.xiaojukeji", DISPATCH_QUEUE_CONCURRENT); // 2.创建任务 void (^task1)() = ^() { NSLog(@"task1---%@", [NSThread currentThread]); }; void (^task2)() = ^() { NSLog(@"task2---%@", [NSThread currentThread]); }; void (^task3)() = ^() { NSLog(@"task3---%@", [NSThread currentThread]); }; // 3.添加任务到并发队列 dispatch_sync(serialSync, task1); dispatch_sync(serialSync, task2); dispatch_sync(serialSync, task3); } 并发队列同步任务 图片 18#pragma mark #pragma mark - 并发队列异步任务 /** 1.打印顺序:无序的 2.在哪条线程上执行:在子线程上执行,每一个任务都在它自己的线程上执行 可以创建N条子线程,它是由底层可调度线程池来决定的,可调度线程池它是有一个重用机制 应用场景 同时下载多个影片 */ - (void)serialAsync { // 1.创建并发队列 dispatch_queue_t serialAsync = dispatch_queue_create("com.xiaojukeji", DISPATCH_QUEUE_CONCURRENT); // 2.创建任务 void (^task1)() = ^() { NSLog(@"task1---%@", [NSThread currentThread]); }; void (^task2)() = ^() { NSLog(@"task2---%@", [NSThread currentThread]); }; void (^task3)() = ^() { NSLog(@"task3---%@", [NSThread currentThread]); }; // 3.将任务添加到并发队列 dispatch_async(serialAsync, task1); dispatch_async(serialAsync, task2); dispatch_async(serialAsync, task3); } 并发队列异步任务

 

self.imageView.image = image;

dispatch_queue_priority_tpriority,//队列的优先级

2018-02-23 20:36:43.774440+0800 YSC-GCD-demo[19929:5005567] 3---{number = 4, name = (null)}

iOS开发之多线程技术——GCD篇,ios多线程gcd

本篇将从四个方面对iOS开发中GCD的使用进行详尽的讲解:


一、什么是GCD
二、我们为什么要用GCD技术
三、在实际开发中如何使用GCD更好的实现我们的需求
  一、Synchronous & Asynchronous 同步 & 异步
  二、Serial Queues & Concurrent Queues 串行 & 并发
  三、Global Queues全局队列
  四、Main Queue主队列
  五、同步的作用
  六、dispatch_time延迟操作
  七、线程安全(单例dispatch_once、读写dispatch_barrier_async)
  八、调度组(dispatch_group)
四、定时源事件和子线程的运行循环

*/

https://github.com/tangbinbinM/GCD-.git

先来看看不考虑线程安全的代码:

一、什么是GCD

  GCD 是基于 C 的 API,它是 libdispatch 的市场名称,而 libdispatch 作为 Apple 的一个库,为并发代码在多核硬件(跑 iOS 或 OS X )上执行提供有力支持。

 


1.什么是GCD?

2018-02-23 20:44:19.377321+0800 YSC-GCD-demo[20083:5040347] currentThread---{number = 3, name = (null)}

********************笔记**************************

dispatch_queue_tqueue =dispatch_get_main_queue();

2018-02-23 20:34:59.099100+0800 YSC-GCD-demo[19892:4996930] 1---{number = 1, name = main}

在iOS6.0之前,在GCD中每当使用带creat单词的函数创建对象之后,都应该对其进行一次release操作。在iOS6.0之后,GCD被纳入到了ARC的内存管理机制中,在使用GCD的时候我们就像对待普通OC对象一样对待GCD,因此不再需要我们调用release方法。

1.有4个术语比较容易混淆:同步、异步、并发、串行

dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会一直等待(阻塞所在线程),否则就可以正常执行。

一次性代码

程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

2018-02-23 22:05:03.790237+0800 YSC-GCD-demo[20494:5183349] group---begin

dispatch_group_t group = dispatch_group_create();

//执行1个耗时的异步操作

2018-02-23 20:35:01.099843+0800 YSC-GCD-demo[19892:4996930] 2---{number = 1, name = main}

队列组

dispatch_async(dispatch_get_main_queue(), ^{

2018-02-23 20:35:05.101750+0800 YSC-GCD-demo[19892:4996930] 3---{number = 1, name = main}

}

GCD的基本使用Dome:

  1. GCD 的其他方法

串行队列

注意:先出者不一定就是最先被CPU调度执行。

弄懂了难理解、绕来绕去的队列+任务之后,我们来学习一个简单的东西:5. GCD 线程间的通信

GCD中获得串行有2种途径

dispatch_apply(count, queue, ^(size_tindex) {});

Grand Central Dispatch(GCD)是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。

1.注意:自动回到当前线程(在哪个线程调的就回到哪个线程)调用self的download方法,并且传递参数

NSLog(@"run-----");

可以使用dispatch_get_global_queue来获取。需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。

dispatch_async(dispatch_queue_tqueue, dispatch_block_tblock);

3.同步和异步的区别

6.5.2 dispatch_group_wait

0.获取一个全局的队列

3.全局并发队列的优先级

注意:并发队列的并发功能只有在异步(dispatch_async)函数下才有效

1-2 GCD中设置队列的优先级

dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

*/- (void)groupNotify {NSLog(@"currentThread---%@",[NSThreadcurrentThread]);// 打印当前线程NSLog(@"group---begin");        dispatch_group_t group =  dispatch_group_create();        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 追加任务1for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程}    });        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 追加任务2for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程}    });        dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"3---%@",[NSThreadcurrentThread]);// 打印当前线程}NSLog(@"group---end");    });}

dispatch_sync(queue, ^{

[NSTimerscheduledTimerWithTimeInterval:2.0target:selfselector:@selector(run)userInfo:nilrepeats:NO];

*/- (void)saleTicketNotSafe {while(1) {if(self.ticketSurplusCount >0) {//如果还有票,继续售卖self.ticketSurplusCount--;NSLog(@"%@", [NSStringstringWithFormat:@"剩余票数:%d 窗口:%@",self.ticketSurplusCount, [NSThreadcurrentThread]]);            [NSThreadsleepForTimeInterval:0.2];        }else{//如果已卖完,关闭售票窗口NSLog(@"所有火车票均已售完");break;        }            }}

{

一.GCD简介

区别并发队列串行队列主队列

 执行1个耗时的异步操作

2.延时执行

2018-02-23 20:41:27.047690+0800 YSC-GCD-demo[20008:5024950] 3---{number = 3, name = (null)}

NSLog;1.获得主队列

六.串行队列创建

6.1 GCD 栅栏方法:dispatch_barrier_async

GCD基本使用

将任务添加到队列中即可

2018-02-23 22:32:29.433615+0800 YSC-GCD-demo[20862:5290689] 剩余票数:2 窗口:{number = 4, name = (null)}

异步函数+主队列:不会开线程,串行执行

- (void)barrier

从dispatch_group_wait相关代码运行输出结果可以看出:

用异步的方式执行任务

});

* 异步执行 + 串行队列

block:任务

//延时2秒,前面两种常用

图片 19

}

NSLog(@"run-----");

* 特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务

 dispatch_queue_t queue, dispatch_block_t

dispatch_group_tgroup =dispatch_group_create();

创建一个队列(串行队列或并发队列)

-once

//前面任务执行完在执行之后在执行后面的任务

同步执行就是,你打电话给小明的时候,不能同时打给小白,等到给小明打完了,才能打给小白(等待任务执行结束)。而且只能用当前的电话(不具备开启新线程的能力)。

NSLog(@"--dispatch_barrier_async-");

4.执行方式

2018-02-23 22:14:22.001339+0800 YSC-GCD-demo[20592:5215095] 2---{number = 3, name = (null)}

2.使用GCD函数(3秒后自动开启新线程 执行block中的代码,不会卡主当前的线程,在主/子线程调用都可以使用)

- (void)once

2018-02-23 20:36:43.774442+0800 YSC-GCD-demo[19929:5005566] 2---{number = 5, name = (null)}

图片 203.png图片 214.png

八.GCD常用函数栅栏

需要注意的是:dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,dispatch_after函数是很有效的。

NSLog;

二.GCD的两要素任务和队列

  1. GCD 任务和队列

GCD中有2个核心概念

});

2018-02-23 20:36:41.769496+0800 YSC-GCD-demo[19929:5005237] asyncConcurrent---begin

dispatch_sync(queue, ^{

1.手动创建串行队列 (DISPATCH_QUEUE_SERIAL)

2018-02-23 22:14:20.000298+0800 YSC-GCD-demo[20592:5215094] 1---{number = 4, name = (null)}

使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次

{

2018-02-23 22:10:20.947790+0800 YSC-GCD-demo[20538:5199138] 1---{number = 3, name = (null)}

-asyncWithConcurrent

Grand Central Dispatch(GCD) 是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用;纯C语言,提供了非常强大的函数。

可以看到在不考虑线程安全,不使用 semaphore 的情况下,得到票数是错乱的,这样显然不符合我们的需求,所以我们需要考虑线程安全问题。

GCD的队列可以分为2大类型

1.任务和队列是GCD的2个核心概念

2018-02-23 22:03:18.476735+0800 YSC-GCD-demo[20470:5177036] 3---{number = 5, name = (null)}

什么是GCD

任务的取出遵循队列的FIFO原则:先进先出,后进后出

2018-02-23 22:03:18.476704+0800 YSC-GCD-demo[20470:5177037] 2---{number = 4, name = (null)}

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

使用dispatch_get_main_queue()获得主队列

*/- (void)groupWait {NSLog(@"currentThread---%@",[NSThreadcurrentThread]);// 打印当前线程NSLog(@"group---begin");        dispatch_group_t group =  dispatch_group_create();        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 追加任务1for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程}    });        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 追加任务2for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程}    });// 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)dispatch_group_wait(group, DISPATCH_TIME_FOREVER);NSLog(@"group---end");}

2.在iOS6.0之前,在GCD中凡是使用了带Crearte和retain的函数在最后都需要做一次release操作。而主队列和全局并发队列不需要我们手动release。当然了,在iOS6.0之后GCD已经被纳入到了ARC的内存管理范畴中,即便是使用retain或者create函数创建的对象也不再需要开发人员手动释放,我们像对待普通OC对象一样对待GCD就OK。

});

输出结果:

[self syncWithMain];

dispatch_once(&onceToken, ^{

/**

withObject:参数

GCD是苹果公司为多核的并行运算提出的解决方案

输出结果为:

});

NSLog(@"----4----%@", [NSThreadcurrentThread]);

2018-02-23 22:10:16.939455+0800 YSC-GCD-demo[20538:5198871] group---begin

dispatch_queue_t queue = dispatch_queue_create("com.520it.download",DISPATCH_QUEUE_SERIAL);

a.并行队列(Concurrent Dispatch Queue):可以让多个任务并行(同时)执行(自动开启多个线程同时执行任务)

除了当前线程(主线程),系统又开启了3个线程,并且任务是交替/同时执行的。(异步执行具备开启新线程的能力。且并发队列可开启多个线程,同时执行多个任务)。

});

并发队列要配合异步函数使用才能起到多线程并发作用。

2018-02-23 20:45:49.982352+0800 YSC-GCD-demo[20111:5046708] asyncMain---end

GCDAPI:

dispatch_release(queue);//非ARC需要释放手动创建的队列

* 特点:可以开启多个线程,任务交替(同时)执行。

1-1 关于GCD中的创建和释放

4.快速迭代

(lldb)

queue:队列

}

2018-02-23 20:39:37.876811+0800 YSC-GCD-demo[19975:5017162] currentThread---{number = 1, name = main}

});

同步:只能在当前线程中执行任务,不具备开启新线程的能力

2018-02-23 20:36:41.769725+0800 YSC-GCD-demo[19929:5005237] asyncConcurrent---end

GCD中的队列也是可以暂停和恢复的,直接把相应的队列作为参数就传递就可以。使用dispatch_resume;和dispatch_suspend;

dispatch_async(queue, ^{

虽然使用 GCD 只需两步,但是既然我们有两种队列(串行队列/并发队列),两种任务执行方式(同步执行/异步执行),那么我们就有了四种不同的组合方式。这四种不同的组合方式是:

GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

放在主队列中的任务,都会放到主线程中执行

2018-02-23 20:48:28.320660+0800 YSC-GCD-demo[20188:5059273] 4---{number = 3, name = (null)}

[self performSelector:@selector withObject:nil afterDelay:2.0];

dispatch_async(queue, ^{

2018-02-23 20:42:36.843050+0800 YSC-GCD-demo[20041:5030982] syncMain---begin

整个程序运行过程中只会执行一次onceToken用来记录该部分的代码是否被执行过

#define DISPATCH_QUEUE_PRIORITY_HIGH2//高

2018-02-23 20:39:43.880660+0800 YSC-GCD-demo[19975:5017162] 2---{number = 1, name = main}

DISPATCH_TIME_NOW:现在开始的意

dispatch_barrier_async(queue, ^{

输出结果:

先调用start方法,在start方法内部会调用main方法。可以通过代码来进行验证。

1.GCD中有两个用来执行任务的函数

iOS GCD之dispatch_semaphore(信号量)

 打印查看当前线程 NSLog(@"刷新UI---%@",[NSThread currentThread]);});

dispatch_group_notify(group,dispatch_get_main_queue(), ^{

下边我们来分别讲讲这几种不同的组合方式的使用方法。

05 队列组

NSLog(@"----1-%@", [NSThreadcurrentThread]);

场景:总共有50张火车票,有两个售卖火车票的窗口,一个是北京火车票售卖窗口,另一个是上海火车票售卖窗口。两个窗口同时售卖火车票,卖完为止。

/**

(第一个参为队列,第二个参为任务)

2018-02-23 22:14:20.000305+0800 YSC-GCD-demo[20592:5215095] 2---{number = 3, name = (null)}

代码案例:-touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event

dispatch_barrier_async(queue, ^{});前面任务先执行,执行完dispatch_barrier_async在执行后面的任务

我们在开发中,会遇到这样的需求:异步执行耗时任务,并使用异步执行的结果进行一些额外的操作。换句话说,相当于,将将异步执行任务转换为同步执行任务。比如说:AFNetworking 中 AFURLSessionManager.m 里面的tasksForKeyPath:方法。通过引入信号量的方式,等待异步执行任务结果,获取到 tasks,然后再返回该 tasks。

3.在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效

//第一种

// 同步执行任务创建方法dispatch_sync(queue, ^{// 这里放同步执行任务代码});// 异步执行任务创建方法dispatch_async(queue, ^{// 这里放异步执行任务代码});

2.0* NSEC_PER_SEC:设置的秒数

{

学习 GCD 之前,先来了解 GCD 中两个核心概念:任务队列

  • sync -- 主队列(不能用---会卡死)

1.手动创建(DISPATCH_QUEUE_CONCURRENT)

链接:

NSLog(@"1---%@",[NSThreadcurrentThread]);

五.并发队列创建

3. GCD 的使用步骤

GCD的优势

//第d三种

我们经常会遇到这样的需求:在指定时间(例如3秒)之后执行某个任务。可以用 GCD 的dispatch_after函数来实现。

同步:在当前线程中执行任务,不具备开启新线程的能力

{

那么这几种不同组合方式各有什么区别呢,这里为了方便,先上结果,再来讲解。你可以直接查看表格结果,然后跳过4. GCD的基本使用

图片 227.png

{

dispatch_semaphore_signal:发送一个信号,让信号总量加1

2.下载图片NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"];NSData*data = [NSData dataWithContentsOfURL:url];UIImage*image = [UIImage imageWithData:data];

dispatch_sync(dispatch_queue_tqueue,dispatch_block_tblock);

同步执行 + 主队列

-asyncWithserial

});

6.3 GCD 一次性代码(只执行一次):dispatch_once

dispatch_async(queue, ^{

三.执行任务

2018-02-23 22:22:28.527030+0800 YSC-GCD-demo[20642:5246341] semaphore---end,number = 100

同步和异步的区别

3.一次性代码

可以看出:在打印asyncMain---begin之后大约 2.0 秒的时间,打印了after---{number = 1, name = main}

static dispatch_once_t onceToken;

Dome:https://github.com/tangbinbinM/GCDFun.git

我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了 GCD 的dispatch_once函数。使用

1-3 暂停和恢复。

dispatch_queue_t

同步执行(sync)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(2.0 * NSEC_PER_SEC)),dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

同步:在当前线程中执行任务,不具备开启新线程的能力

考虑线程安全的代码:

 等前面的异步操作都执行完毕后,回到主线程...

即想做的事情

2018-02-23 22:32:29.230110+0800 YSC-GCD-demo[20862:5290687] 剩余票数:3 窗口:{number = 3, name = (null)}

 NSLog(@"-----下载图片3---%@", [NSThread currentThread]);});NSLog(@"syncMainQueue----end--");

所以需要队列组的解决方案

*/- (void)syncSerial {NSLog(@"currentThread---%@",[NSThreadcurrentThread]);// 打印当前线程NSLog(@"syncSerial---begin");dispatch_queue_tqueue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);dispatch_sync(queue, ^{// 追加任务1for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程}    });dispatch_sync(queue, ^{// 追加任务2for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程}    });dispatch_sync(queue, ^{// 追加任务3for(inti =0; i <2; ++i) {            [NSThreadsleepForTimeInterval:2];// 模拟耗时操作NSLog(@"3---%@",[NSThreadcurrentThread]);// 打印当前线程}    });NSLog(@"syncSerial---end");}

4.其它区别涉及到XUN内核的系统级线程编程,不一一列举。

[selfperformSelector:@selector(run)withObject:nilafterDelay:2.0];

2018-02-23 22:32:29.024817+0800 YSC-GCD-demo[20862:5290689] 剩余票数:4 窗口:{number = 4, name = (null)}

本文由必威发布于必威-编程,转载请注明出处:同步执行和异步执行,这大概是史上最详细、清

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。