它是一个在线程池格局的基本功上执行的面世职务,大家任重而道远讲了

在上一篇中,大家根本讲了Dispatch
Queue
有关的始末。那篇首要讲一下有些和骨子里相关的利用实例,Dispatch
Groups和Dispatch Semaphore。

原文:https://www.jianshu.com/p/2d57c72016c6

dispatch_after

在大家开发进程中平常会用到在有些秒后实施某个方法,平时大家会用这一个- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay函数。然则现在我们可以利用一个新的法子。

dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
    dispatch_after(delayTime, dispatch_get_main_queue(), ^{
        //do your task
    });

那般我们就定义了一个延缓2秒后举行的职分。不过在此间有某些索要表明的是,无论你用的是- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay还是dispatch_after以此艺术。并不是说在您指定的推移后登时运行,这么些方法都是根据单线程的,它只是将您延缓的操作参与到行列之中去。由于队列之中都是FIFO,所以必须在你这几个义务之前的操作完毕后才会履行你的不二法门。那一个延迟只是大略的延期。即使你在主线程里面调用这些方式,若是您主线程现在正在处理一个卓殊耗时的职务,那么您这一个延迟可能就会偏向很大。那个时候你可以再开个线程,在里边实践你的推移操作。

//放到全局默认的线程里面,这样就不必等待当前调用线程执行完后再执行你的方法
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC);
    dispatch_after(delayTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //do your task
    });

概念

Grand Central Dispatch(GCD)是 Apple
开发的一个多核编程的较新的化解措施。它最主要用来优化应用程序以支撑多核处理器以及任何对称多处理系统。它是一个在线程池方式的底蕴上推行的产出任务。

dispatch_once

其一可能大家都特其他熟知,这些在单例伊始化的时候是苹果官方推荐的不二法门。这一个函数可以有限支撑在应用程序中只举行指定的任务一遍。即便在八线程的条件下执行,也得以有限支撑百分之百的平安。

    static id instance;
    static dispatch_once_t predicate;

    dispatch_once(&predicate, ^{
        //your init
    });

    return instance;
}

那中间的predicate不可能不是全局或者静态对象。在二十四线程下同时做客时,那一个措施将被线程同步等待,直到指定的block执行到位。

优点

  • GCD 可用于多核的并行运算
  • GCD 会自动利用更加多的 CPU 内核(比如双核、四核)
  • GCD 会自动管理线程的生命周期(创设线程、调度义务、销毁线程)
  • 程序员只须要告诉 GCD 想要执行如何职务,不需求编制任何线程管理代码

dispatch_apply

以此艺术是实施循环次数固定的迭代,即使在产出的queue里面可以增进性能。比如一个稳定次数的for循环

for (int i = 0; i < 1000; i ++) {
        NSLog(@"---%d---", i);
    }

比方只是在一个线程里面或者在一个串行的队列中是一律的,一个个实践。
现在大家用dispatch_apply来写这么些轮回:

dispatch_apply([array count], defaultQueue, ^(size_t i) {
        NSLog(@"----%@---", array[i]);
    });
    NSLog(@"end");

以此办法执行后,它将像那几个并发队列中不断的提交实施的block。那些i是从0开始的,最后一个是[array count] - 1

动用这一个艺术有几个注意点:

  1. 其一法子调用的时候会卡住当前的线程,也就是上边的大循环全部推行达成后,才会输出end
  2. 在你利用那一个职分展开操作的时候,你应有保障您要实践的依次任务是单身的,而且实施种种也是无足挂齿的。
  3. 在您利用那些主意的时候,你如故要衡量下完整的性质的,假使你执行的职务时间比线程切换的时光还短。那就进寸退尺了。

任务和队列

dispatch_group

在骨子里支出中,我们可能需要在一组操作全部完事后,才做其它操作。比如上传一组图片,或者下载多少个公文。希望在漫天到位时给用户一个提醒。倘使那些操作在串行化的队列中执行的话,那么您可以很扎眼的知晓,当最后一个职分履行到位后,就全体已毕了。那样的操作也并木有发挥二十四线程的优势。大家得以在产出的行列中举行那些操作,可是那一个时候大家就不驾驭哪些是终极一个到位的了。这几个时候大家可以凭借dispatch_group:

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, defaultQueue, ^{
        //task1
        NSLog(@"1");
    });
    dispatch_group_async(group, defaultQueue, ^{
        //task2
        NSLog(@"2");
    });
    dispatch_group_async(group, defaultQueue, ^{
        //task3
        NSLog(@"3");
    });
    dispatch_group_async(group, defaultQueue, ^{
        //task4
        NSLog(@"4");
    });
    dispatch_group_async(group, defaultQueue, ^{
        //task5
        NSLog(@"5");
    });

    dispatch_group_notify(group, queue, ^{
        NSLog(@"finish");
    });

俺们第一创立一个group下一场往里面出席大家要推行的操作,在dispatch_group_notify其一函数里面添加全套做到的操作。上面代码执行的时候,输出的1,2,3,4,5的相继是不自然的,不过出口的finish一定是在1,2,3,4,5后头。
对此拉长到group的操作还有其余一个方法:

    dispatch_group_enter(group);
    dispatch_group_enter(group);

    dispatch_async(defaultQueue, ^{
        NSLog(@"1");
        dispatch_group_leave(group);
    });

    dispatch_async(defaultQueue, ^{
        NSLog(@"2");
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, queue, ^{
        NSLog(@"finish");
    });

大家得以用dispatch_group_enter来代表添加职责,dispatch_group_leave来代表有个职责现已已毕了。用那一个艺术自然要专注必须成双成对。

任务

职分就是实施操作的意味,换句话说就是线程内实施的代码,在 GCD 中位居
block 里。
进行任务的法门有两种:一起施行(sync)和异步执行(async),两者的主要分化是:是或不是等待队列里的职责完结以及是不是具有开启新线程的力量

  • 手拉手实施(sync)
    共同添加职责到指定的行列中,在抬高的天职履行完成从前,会一贯守候,直到队列之中的任务已毕未来再继续执行。只可以在此时此刻线程中施行职务,不抱有开启新线程的能力。
  • 异步执行(async)
    异步添加职务到指定的连串中,它不会做任何等待,可以继续执行任务。可以在新的线程中实施职务,具备开启新线程的能力。

留神:异步执行(async)尽管富有开启新线程的力量,不过并不一定开启新线程。那跟任务所指定的队列类型有关(上边会讲)。

线程同步

在十二线程中一个相比较首要的东西就是线程同步的题材。如若三个线程只是对某个资源只是读的历程,那么就不设有这么些问题了。若是某个线程对那几个资源须求举办写的操作,那这些时候就会并发数量区其余问题了。

队列

那里的队列指执行职务的等候队列,即用来存放在任务的队列。队列是一种新鲜的线性表,选用FIFO(先进先出)的标准,即新职分接二连三被插入到行列的末梢,而读取义务的时候总是从队列的头顶早先读取。每读取一个职分,则从队列中释放一个职务。
GCD
中的职责有二种:串行队列并发队列,两者都适合先入先出标准,分歧在于:执行顺序分化和开启线程数分裂。

  • 串行队列(Serial Dispatch Queue)
    只开启一个线程,每一回只举办一个任务,执行完那一个义务连续下一个职务。
  • 并发队列(Concurrent Dispatch Queue)
    可以在三个线程中,让三个义务并发(同时)执行。注意:并发队列的面世作用只有在异步(dispatch_async)函数下才有效

使用dispatch_barrier_async

    __block NSString *strTest = @"test";

    dispatch_async(defaultQueue, ^{
        if ([strTest isEqualToString:@"test"]) {
            NSLog(@"--%@--1-", strTest);
            [NSThread sleepForTimeInterval:1];
            if ([strTest isEqualToString:@"test"]) {
                [NSThread sleepForTimeInterval:1];
                NSLog(@"--%@--2-", strTest);
            } else {
                NSLog(@"====changed===");
            }
        }
    });
    dispatch_async(defaultQueue, ^{
        NSLog(@"--%@--3-", strTest);
    });
    dispatch_async(defaultQueue, ^{
        strTest = @"modify";
        NSLog(@"--%@--4-", strTest);
    });

探望那个宪章的情况,大家让各类线程去拜谒这么些变量,其中有个操作是要修改这么些变量。我们把第二个操作先判断有木有变动,然后故意推迟一下,那一个时候大家看下输出结果:

2015-01-03 15:42:21.351 测试[1652:60015] --test--3-
2015-01-03 15:42:21.351 测试[1652:60013] --modify--4-
2015-01-03 15:42:21.351 测试[1652:60014] --test--1-
2015-01-03 15:42:22.355 测试[1652:60014] ====changed===

我们得以看到,再一次判断的时候,已经被改动了,若是我们在实际的事情中那样去判断某些重点的变量,可能就会油可是生严重的题材。上边看看大家如何利用dispatch_barrier_async来展开共同:

 //并发队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);

    __block NSString *strTest = @"test";

    dispatch_async(concurrentQueue, ^{
        if ([strTest isEqualToString:@"test"]) {
            NSLog(@"--%@--1-", strTest);
            [NSThread sleepForTimeInterval:1];
            if ([strTest isEqualToString:@"test"]) {
                [NSThread sleepForTimeInterval:1];
                NSLog(@"--%@--2-", strTest);
            } else {
                NSLog(@"====changed===");
            }
        }
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"--%@--3-", strTest);
    });
    dispatch_barrier_async(concurrentQueue, ^{
        strTest = @"modify";
        NSLog(@"--%@--4-", strTest);
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"--%@--5-", strTest);
    });

当今看下输出结果:

2015-01-03 16:00:27.552 测试[1786:65947] --test--1-
2015-01-03 16:00:27.552 测试[1786:65965] --test--3-
2015-01-03 16:00:29.553 测试[1786:65947] --test--2-
2015-01-03 16:00:29.553 测试[1786:65947] --modify--4-
2015-01-03 16:00:29.553 测试[1786:65947] --modify--5-

明日我们得以发现操作4用dispatch_barrier_async进入操作后,前面的操作3以前都操作完结以前这些strTest都并未变。而背后的操作都是改变后的值。那样大家的数量顶牛的问题就解决了。
当今表明下那一个函数干的事体,当那一个函数加入到行列后,里面block并不是马上施行的,它会先等待以前正在实践的block全部完结后,才实施,并且在它之后进入到行列中的block也在它操作截止后才能上涨以前的产出执行。大家得以把那个函数了然为一条分割线,之前的操作,之后参预的操作。还有一个点要证实的是这些queue必须是用dispatch_queue_create创设出来的才行。

多少个至关紧要

使用Dispatch Semaphore

dispatch_semaphore_t 类似信号量,能够用来决定访问某一资源访问数量。
动用进程:

  1. 先创设一个Dispatch Semaphore对象,用整数值表示资源的可用数量
  2. 在各类义务中,调用dispatch_semaphore_wait来等待
  3. 取得资源就可以进行操作
  4. 操作完后调用dispatch_semaphore_signal来释放资源

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    __block NSString *strTest = @"test";

    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        if ([strTest isEqualToString:@"test"]) {
            NSLog(@"--%@--1-", strTest);
            [NSThread sleepForTimeInterval:1];
            if ([strTest isEqualToString:@"test"]) {
                [NSThread sleepForTimeInterval:1];
                NSLog(@"--%@--2-", strTest);
            } else {
                NSLog(@"====changed===");
            }
        }
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--3-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        strTest = @"modify";
        NSLog(@"--%@--4-", strTest);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_async(concurrentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"--%@--5-", strTest);
        dispatch_semaphore_signal(semaphore);
    });

那般我们一致可以确保,线程的多寡安全。

队列与线程的涉及:

主队列(main_queue)就是在主线程中的默许队列。并发队列(global_queue)就是新开拓线程中的队列。
一个线程中可以有多个串行的队列,不过力不从心兼而有之三个冒出的种类,几个冒出的行列会放在七个线程中。例如可以自定义队列加到主线程中同步实施,中断主队列职务履行完自定义队列中义务后继续执行主队列职分。

异步与产出的区分:

异步,是指执行各样上,不相同于一块的贯通,异步会延后执行异步内的职责。而产出是指时间上的,分歧于串行的等候,并发会同时进行并发队列中的职分。

GCD的运用手续

GCD的选拔有两步,第一步创立一个行列(串行队列或者并行队列),第二步将职分加入到等候队列中。

创办队列

一般说来采纳dispatch_queue_create(<#const char * _Nullable label#>, <#dispatch_queue_attr_t _Nullable attr#>)来创立队列。第二个参数表示队列的唯一标识符,平常用逆序全程域名;第四个参数用来甄别是串行队列照旧出现队列:DISPATCH_QUEUE_SERIAL
表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列。

//创建串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("com.dispatch.cp", DISPATCH_QUEUE_SERIAL);
//创建并发队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.dispatch.cp", DISPATCH_QUEUE_CONCURRENT);
赢得队列

对此串行队列,GCD 提供了一种默许的与众不一致的串行队列:主队列(Main
Dispatch Queue)

有着放在主队列中的义务,都会安置主线程中举办。可应用
dispatch_get_main_queue() 得到主队列。

对此出现队列,GCD 供了一种默许的万分的面世队列:全局并发队列(Global
Dispatch Queue)

能够动用 dispatch_get_global_queue
来获取。须要传入几个参数。第二个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第三个参数暂时没用,用0即可。

//获取默认串行队列(主队列)
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//获取默认并发队列 (全局并发队列)
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
创造职责

GCD 提供了一道执行职务的开创方法 dispatch_sync 和异步执行义务成立方法
dispatch_async

//创建同步任务
dispatch_sync(<#dispatch_queue_t  _Nonnull queue#>, ^{
    //这里放任务的执行方法
})

//创建异步任务
dispatch_async(<#dispatch_queue_t  _Nonnull queue#>, ^{
    //这里放任务的执行方法
})

尽管应用 GCD
只需两步,然则既然我们有两种队列(串行队列/并发队列),二种职务执行办法(同步施行/异步执行),那么我们就有了四种区其余咬合措施。实际上,刚才还说了三种特有队列:全局并发队列、主队列。全局并发队列可以当做平时并发队列来使用。可是主队列因为微微异样,所以我们就又多了三种组成措施。那样就有六种分化的构成格局了。

  • 手拉手实施 + 并发队列
    无效。同步无法开启新线程,无法兑现产出。
  • 异步执行 + 并发队列
    八线程的常用方式,异步并发,十二线程同时开展七个义务。
  • 一路实施 + 串行队列
    平凡串行,没有用到多线程。
  • 异步执行 + 串行队列
    三十二线程的常用方式,异步串行,新建一个线程串行执行多少个义务。
  • 一同实施 + 主队列
    不算。新建职务需等候主队列任务完成,主队列后续任务需拭目以待新建职责,相互等待,代码卡死崩溃。
  • 异步执行 + 主队列
    类似异步串行,只是没有新开拓线程,在主线程完毕异步。

GCD六种组成下的状态

1.一并实施 + 并发队列

代码:

- (void)syncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]); //打印当前线程
    NSLog(@"syncConcurrent---begin");
    //创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    //同步任务 + 并发队列
    dispatch_sync(queue, ^{
        // 自定义任务1
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2]; //模拟耗时操作
            NSLog(@"任务1---%@",[NSThread currentThread]); //打印当前线程

        }

    });
    dispatch_sync(queue, ^{
        // 自定义任务2
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2]; //模拟耗时操作
            NSLog(@"任务2---%@",[NSThread currentThread]);// 打印当前线程

        }

    });
    dispatch_sync(queue, ^
    {
        // 自定义任务3
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2]; //模拟耗时操作
            NSLog(@"任务3---%@",[NSThread currentThread]);// 打印当前线程

        }

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

}

log:

2018-04-08 15:07:13.433670+0800 应用加载时间的优化[5701:894068] currentThread---<NSThread: 0x600000076180>{number = 1, name = main}
2018-04-08 15:07:13.434007+0800 应用加载时间的优化[5701:894068] syncConcurrent---begin
2018-04-08 15:07:15.436162+0800 应用加载时间的优化[5701:894068] 任务1---<NSThread: 0x600000076180>{number = 1, name = main}
2018-04-08 15:07:17.437191+0800 应用加载时间的优化[5701:894068] 任务1---<NSThread: 0x600000076180>{number = 1, name = main}
2018-04-08 15:07:19.438426+0800 应用加载时间的优化[5701:894068] 任务2---<NSThread: 0x600000076180>{number = 1, name = main}
2018-04-08 15:07:21.439689+0800 应用加载时间的优化[5701:894068] 任务2---<NSThread: 0x600000076180>{number = 1, name = main}
2018-04-08 15:07:23.441261+0800 应用加载时间的优化[5701:894068] 任务3---<NSThread: 0x600000076180>{number = 1, name = main}
2018-04-08 15:07:25.442858+0800 应用加载时间的优化[5701:894068] 任务3---<NSThread: 0x600000076180>{number = 1, name = main}
2018-04-08 15:07:25.443222+0800 应用加载时间的优化[5701:894068] syncConcurrent---end

可以见见线程数一向为1,当前线程一贯为主线程
总括:并发队列本身不可能成立线程,而一同义务一样没有开创线程。因而平素为主线程同步效果,没有落到实处产出。

2.异步执行 + 并发队列

代码:

- (void)asyncConcurrent {
    NSLog(@"currentThread---%@",[NSThread currentThread]);// 打印当前线程
    NSLog(@"asyncConcurrent---begin");
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        // 自定义任务1
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2]; //模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 自定义任务2
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2]; //模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);// 打印当前线程

        }

    });
    dispatch_async(queue, ^{
        // 自定义任务3
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2]; //模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);// 打印当前线程
        }

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

log:

2018-04-08 22:01:41.497508+0800 应用加载时间的优化[6217:973574] currentThread---<NSThread: 0x604000078780>{number = 1, name = main}
2018-04-08 22:01:41.497657+0800 应用加载时间的优化[6217:973574] asyncConcurrent---begin
2018-04-08 22:01:41.498239+0800 应用加载时间的优化[6217:973574] asyncConcurrent---end
2018-04-08 22:01:44.622924+0800 应用加载时间的优化[6217:973669] 3---<NSThread: 0x600000277200>{number = 4, name = (null)}
2018-04-08 22:01:44.622929+0800 应用加载时间的优化[6217:973666] 2---<NSThread: 0x604000463440>{number = 3, name = (null)}
2018-04-08 22:01:46.624147+0800 应用加载时间的优化[6217:973667] 1---<NSThread: 0x604000461f40>{number = 5, name = (null)}
2018-04-08 22:01:46.624234+0800 应用加载时间的优化[6217:973666] 2---<NSThread: 0x604000463440>{number = 3, name = (null)}
2018-04-08 22:01:46.624266+0800 应用加载时间的优化[6217:973669] 3---<NSThread: 0x600000277200>{number = 4, name = (null)}
2018-04-08 22:01:48.626508+0800 应用加载时间的优化[6217:973667] 1---<NSThread: 0x604000461f40>{number = 5, name = (null)}

总计:可以看看开辟了两个线程(不亮堂干什么不是多个),职务交替/同时展开。异步拥有开启新线程的力量,并发执行职务。第一时间在主线程中履行了syncConcurrent---begin
syncConcurrent---end
,然后在开启的几个线程中并且实施自定义的打印职务,然后在延迟三分钟后再同时打印第二次。

3.手拉手执行 + 串行队列

代码:

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

        }
    });
    dispatch_sync(queue, ^{
        // 追加任务2
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);// 打印当前线程
        }

    });
    dispatch_sync(queue, ^{
        // 追加任务3
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    NSLog(@"syncSerial---end");
}

log:

2018-04-08 22:27:00.564340+0800 应用加载时间的优化[6278:994824] currentThread---<NSThread: 0x604000078d40>{number = 1, name = main}
2018-04-08 22:27:00.564452+0800 应用加载时间的优化[6278:994824] syncSerial---begin
2018-04-08 22:27:02.565885+0800 应用加载时间的优化[6278:994824] 1---<NSThread: 0x604000078d40>{number = 1, name = main}
2018-04-08 22:27:04.566469+0800 应用加载时间的优化[6278:994824] 1---<NSThread: 0x604000078d40>{number = 1, name = main}
2018-04-08 22:27:06.568109+0800 应用加载时间的优化[6278:994824] 2---<NSThread: 0x604000078d40>{number = 1, name = main}
2018-04-08 22:27:08.569092+0800 应用加载时间的优化[6278:994824] 2---<NSThread: 0x604000078d40>{number = 1, name = main}
2018-04-08 22:27:10.569759+0800 应用加载时间的优化[6278:994824] 3---<NSThread: 0x604000078d40>{number = 1, name = main}
2018-04-08 22:27:12.571093+0800 应用加载时间的优化[6278:994824] 3---<NSThread: 0x604000078d40>{number = 1, name = main}
2018-04-08 22:27:12.571398+0800 应用加载时间的优化[6278:994824] syncSerial---end

小结:所有职分都在主线程中,没有开发新的线程。职责由上至下,间隔两秒,依次执行。

4.异步执行 + 串行队列

代码:

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

log:

2018-04-08 22:33:07.135609+0800 应用加载时间的优化[6314:1001768] currentThread---<NSThread: 0x604000073b80>{number = 1, name = main}
2018-04-08 22:33:07.136200+0800 应用加载时间的优化[6314:1001768] asyncSerial---begin
2018-04-08 22:33:07.136992+0800 应用加载时间的优化[6314:1001768] asyncSerial---end
2018-04-08 22:33:09.137247+0800 应用加载时间的优化[6314:1001860] 1---<NSThread: 0x60400027b7c0>{number = 3, name = (null)}
2018-04-08 22:33:11.142105+0800 应用加载时间的优化[6314:1001860] 1---<NSThread: 0x60400027b7c0>{number = 3, name = (null)}
2018-04-08 22:33:13.143027+0800 应用加载时间的优化[6314:1001860] 2---<NSThread: 0x60400027b7c0>{number = 3, name = (null)}
2018-04-08 22:33:15.145256+0800 应用加载时间的优化[6314:1001860] 2---<NSThread: 0x60400027b7c0>{number = 3, name = (null)}
2018-04-08 22:33:17.147141+0800 应用加载时间的优化[6314:1001860] 3---<NSThread: 0x60400027b7c0>{number = 3, name = (null)}
2018-04-08 22:33:19.151007+0800 应用加载时间的优化[6314:1001860] 3---<NSThread: 0x60400027b7c0>{number = 3, name = (null)}

小结:在主线程中第一时间syncConcurrent---begin
syncConcurrent---end,然后在拉开的新线程中串行执行自定义义务,依次间隔两秒。

5.合办施行 + 主队列

代码:

- (void)syncMain
{
    NSLog(@"currentThread---%@",[NSThread currentThread]);// 打印当前线程
    NSLog(@"syncMain---begin");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(dispatch_get_main_queue(), ^{
        // 追加任务1
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务2
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务3
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    NSLog(@"syncMain---end");
}

运作时会直接崩溃。
崩溃原因:那是因为我们在主线程中实施 syncMain 方法,相当于把 syncMain
职分放到了主线程的主队列中。而一起执行会等待眼前队列中的职分执行达成,才会随着执行。那么当大家把职分1日增到主队列中,职分1就在等候主线程处理完
syncMain 任务。而 syncMain 职责须求拭目以待任务1实践已毕,才能随着执行。
那就是说,现在的情形就是syncMain职分和天职1都在等对方执行已毕。那样大家互动等待,所以就卡住了,所以我们的职务履行不断。
而从前的协同串行和一道并发都可以执行不会崩溃的缘由在于,他们就算都在主线程中,但是并不在主队列(main_queue)中,而是在自定义的队列中。

6.异步举行 + 主队列

代码:

- (void)asyncMain {
    NSLog(@"currentThread---%@",[NSThread currentThread]);// 打印当前线程
    NSLog(@"asyncMain---begin");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        // 追加任务1
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);// 打印当前线程
        }
    });
    NSLog(@"asyncMain---end");
}

log:

2018-04-08 23:14:51.902008+0800 应用加载时间的优化[6448:1043174] currentThread---<NSThread: 0x600000076a00>{number = 1, name = main}
2018-04-08 23:14:51.902183+0800 应用加载时间的优化[6448:1043174] asyncMain---begin
2018-04-08 23:14:51.902511+0800 应用加载时间的优化[6448:1043174] asyncMain---end
2018-04-08 23:14:53.906967+0800 应用加载时间的优化[6448:1043174] 1---<NSThread: 0x600000076a00>{number = 1, name = main}
2018-04-08 23:14:55.907579+0800 应用加载时间的优化[6448:1043174] 1---<NSThread: 0x600000076a00>{number = 1, name = main}
2018-04-08 23:14:57.909015+0800 应用加载时间的优化[6448:1043174] 2---<NSThread: 0x600000076a00>{number = 1, name = main}
2018-04-08 23:14:59.909606+0800 应用加载时间的优化[6448:1043174] 2---<NSThread: 0x600000076a00>{number = 1, name = main}
2018-04-08 23:15:01.910556+0800 应用加载时间的优化[6448:1043174] 3---<NSThread: 0x600000076a00>{number = 1, name = main}
2018-04-08 23:15:03.912148+0800 应用加载时间的优化[6448:1043174] 3---<NSThread: 0x600000076a00>{number = 1, name = main}

统计:类似于异步执行串行队列,都是在第一时间执行完syncConcurrent---begin

syncConcurrent---end后按顺序间隔两秒执行义务。分裂之处在于尚未开发新的线程,而是在主线程中异步执行。
故此参照下面所说的异步与产出的涉及,实际上异步使自定义的任务延后执行,整个线程仍是在主队列中串行执行自定义任务。而其效果,与异步串行队列效果等同,都是异步串行,根本不一致在于前者将义务放在主线程的主队列中,而后者将职责在子线程的自定义队列中,也就是前者唯有一个线程,后者多开发了一个线程。

线程间通讯

在iOS开发进度中,大家一般在主线程里边举行UI刷新,例如:点击、滚动、拖拽等事件。大家平常把有些耗时的操作放在别的线程,比如说图片下载、文件上传等耗时操作。而当大家偶尔在任何线程完毕了耗时操作时,须要重回主线程,那么就用到了线程之间的通信。
使用dispatch_async(dispatch_get_main_queue(), ^{ });回去主队列。
代码:

- (void)communication {
    // 获取全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    // 获取主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        // 异步追加任务
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);// 打印当前线程
        }
        // 回到主线程
        dispatch_async(mainQueue, ^{
            // 追加在主线程中执行的任务
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);// 打印当前线程
        });
    });
}

log:

2018-04-08 23:37:32.260716+0800 应用加载时间的优化[6512:1064267] 1---<NSThread: 0x6040004613c0>{number = 3, name = (null)}
2018-04-08 23:37:34.263732+0800 应用加载时间的优化[6512:1064267] 1---<NSThread: 0x6040004613c0>{number = 3, name = (null)}
2018-04-08 23:37:36.265455+0800 应用加载时间的优化[6512:1064179] 2---<NSThread: 0x600000078780>{number = 1, name = main}

总计:可以看来在其他线程中先实施职分,执行完通晓后重回主线程执行主线程的相应操作。

GCD的别样常用方法

栅栏方法:dispatch_barrier_async

大家偶尔须要异步执行两组操作,而且首先组操作实践完事后,才能初叶施行第二组操作。那样大家就要求一个一定于栅栏一样的一个主意将两组异步执行的操作组给分割起来,当然那里的操作组里可以分包一个或多少个任务。那就须求用到
dispatch_barrier_async 方法在几个操作组间形成栅栏。
代码:

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

log:

2018-04-10 18:52:30.142820+0800 应用加载时间的优化[7378:1317650] barrier---begin
2018-04-10 18:52:30.143018+0800 应用加载时间的优化[7378:1317650] barrier---end
2018-04-10 18:52:32.147032+0800 应用加载时间的优化[7378:1317775] 1---<NSThread: 0x604000075880>{number = 4, name = (null)}
2018-04-10 18:52:32.147034+0800 应用加载时间的优化[7378:1317777] 2---<NSThread: 0x600000274500>{number = 3, name = (null)}
2018-04-10 18:52:34.148931+0800 应用加载时间的优化[7378:1317777] 2---<NSThread: 0x600000274500>{number = 3, name = (null)}
2018-04-10 18:52:34.148931+0800 应用加载时间的优化[7378:1317775] 1---<NSThread: 0x604000075880>{number = 4, name = (null)}
2018-04-10 18:52:36.149834+0800 应用加载时间的优化[7378:1317775] barrier---<NSThread: 0x604000075880>{number = 4, name = (null)}
2018-04-10 18:52:38.151610+0800 应用加载时间的优化[7378:1317775] barrier---<NSThread: 0x604000075880>{number = 4, name = (null)}
2018-04-10 18:52:40.153601+0800 应用加载时间的优化[7378:1317775] 3---<NSThread: 0x604000075880>{number = 4, name = (null)}
2018-04-10 18:52:40.153601+0800 应用加载时间的优化[7378:1317777] 4---<NSThread: 0x600000274500>{number = 3, name = (null)}
2018-04-10 18:52:42.155339+0800 应用加载时间的优化[7378:1317775] 3---<NSThread: 0x604000075880>{number = 4, name = (null)}
2018-04-10 18:52:42.155339+0800 应用加载时间的优化[7378:1317777] 4---<NSThread: 0x600000274500>{number = 3, name = (null)}

总计:由于是异步,所有职务都异步执行。参预了栅栏,所有异步任务先实施栅栏前,再实践栅栏,最后执行栅栏后。

延时艺术:dispatch_after

在指定时间(例如3秒)之后执行某个职务。可以用 GCD 的 dispatch_after
函数来促成。
须求留意的是:dispatch_after
函数并不是在指定时间过后才起首实践拍卖,而是在指定时间之后将义务增多到主队列中。严谨来说,这些时间并不是绝对可信的,但想要大概延迟执行义务,dispatch_after
函数是很得力的。
代码:

- (void)after {
    NSLog(@"currentThread---%@",[NSThread currentThread]);// 打印当前线程
    NSLog(@"after---begin");
    // 2.0秒后异步追加任务代码到主队列,并开始执行
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"after---%@",[NSThread currentThread]);// 打印当前线程
    });
    NSLog(@"after---end");
}

log:

2018-04-10 19:08:03.125108+0800 应用加载时间的优化[7439:1332291] currentThread---<NSThread: 0x600000077e40>{number = 1, name = main}
2018-04-10 19:08:03.125318+0800 应用加载时间的优化[7439:1332291] after---begin
2018-04-10 19:08:03.125458+0800 应用加载时间的优化[7439:1332291] after---end
2018-04-10 19:08:05.125544+0800 应用加载时间的优化[7439:1332291] after---<NSThread: 0x600000077e40>{number = 1, name = main}

总括:可以观察是异步执行,没有卡主线程。延迟两秒后举办 block 内义务。

只进行一回代码:dispatch_once

我们在开立单例、或者有整套程序运行进度中只进行三次的代码时,大家就用到了
GCD 的 dispatch_once 函数。使用 dispatch_once
函数能确保某段代码在程序运行进度中只被实践1次,并且固然在多线程的条件下,dispatch_once
也可以保障线程安全。
代码:

+ (instancetype)instance
{

    static CPReachability *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      instance = [[self alloc] init];
      instance.netType = CPAPPNetTypeWIFI;
      instance.wifiName = [instance GetWifiName];
    });
    return instance;
}

统计:多用于单例。dispatch_once_t 其实是 long
类型,取其地址作为唯一标识符有限支撑 block 内部职责履行且仅被实施两次。

迅猛迭代方法:dispatch_apply

日常大家会用 for 循环遍历,可是 GCD 给大家提供了长足迭代的函数
dispatch_apply 。dispatch_apply
根据指定的次数将点名的天职增多到指定的体系中,并听候全体种类执行完结。
俺们可以利用异步队列同时遍历。比如说遍历 0~5 那6个数字,for
循环的做法是每趟取出一个因素,逐个遍历。dispatch_apply
可以而且遍历多少个数字。
代码:

- (void)apply {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    NSLog(@"apply---begin");
    dispatch_apply(6, queue, ^(size_t index) {
        [NSThread sleepForTimeInterval:2];//模拟耗时操作
        NSLog(@"%zd---%@",index, [NSThread currentThread]);
    });
    NSLog(@"apply---end");
}

log:

2018-04-10 19:18:38.640868+0800 应用加载时间的优化[7506:1345125] apply---begin
2018-04-10 19:18:40.642368+0800 应用加载时间的优化[7506:1345125] 0---<NSThread: 0x6000000714c0>{number = 1, name = main}
2018-04-10 19:18:40.646225+0800 应用加载时间的优化[7506:1345197] 3---<NSThread: 0x600000265e80>{number = 5, name = (null)}
2018-04-10 19:18:40.646225+0800 应用加载时间的优化[7506:1345215] 1---<NSThread: 0x60400046c2c0>{number = 4, name = (null)}
2018-04-10 19:18:40.646272+0800 应用加载时间的优化[7506:1345198] 2---<NSThread: 0x600000265800>{number = 3, name = (null)}
2018-04-10 19:18:42.643982+0800 应用加载时间的优化[7506:1345125] 4---<NSThread: 0x6000000714c0>{number = 1, name = main}
2018-04-10 19:18:42.647137+0800 应用加载时间的优化[7506:1345197] 5---<NSThread: 0x600000265e80>{number = 5, name = (null)}
2018-04-10 19:18:42.647443+0800 应用加载时间的优化[7506:1345125] apply---end

总括:首先大家能看到 dispatch_apply 是出新执行的,因为在 queue
中,所以不可以有限支持实施种种。然而结果是一头的,会等待所有任务落成后继续线程,最后执行
apply---end。优点在于大规模循环时比起 for
成效更高,比起手动开线程能幸免线程数过多导致线程爆炸。
应用场景:
一旦大家从服务器获取一个数组的数量,那么我们得以动用该措施从而火速的批量字典转模型。

dispatch_group

dispatch_group 是 GCD
中的一组方法,他有一个组的概念,可以把相关的任务归并到一个组内来实施,通过监听组内所有职责的履行景况来做相应处理。

dispatch_group_create

用来创建任务组
dispatch_group_t dispatch_group_create(void);

dispatch_group_async

把异步任务交给到指定义务组和指定下拿出行列执行

void dispatch_group_async(dispatch_group_t group,
                          dispatch_queue_t queue,
                          dispatch_block_t block);
  • group
    ——对应的天职组,之后可以透过dispatch_group_wait或者dispatch_group_notify监听职务组内职分的实施情形
  • queue ——block义务执行的线程队列,任务组内差异职务的行列可以分裂
  • block —— 执行任务的block
dispatch_group_notify

待任务组执行已毕时调用,不会卡住当前线程
代码:

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

log:

2018-04-10 19:31:53.446408+0800 应用加载时间的优化[7570:1360370] currentThread---<NSThread: 0x600000075ac0>{number = 1, name = main}
2018-04-10 19:31:53.446553+0800 应用加载时间的优化[7570:1360370] group---begin
2018-04-10 19:31:55.450356+0800 应用加载时间的优化[7570:1360470] 1---<NSThread: 0x600000272640>{number = 3, name = (null)}
2018-04-10 19:31:55.450352+0800 应用加载时间的优化[7570:1360463] 2---<NSThread: 0x600000273c00>{number = 4, name = (null)}
2018-04-10 19:31:57.450882+0800 应用加载时间的优化[7570:1360470] 1---<NSThread: 0x600000272640>{number = 3, name = (null)}
2018-04-10 19:31:57.450882+0800 应用加载时间的优化[7570:1360463] 2---<NSThread: 0x600000273c00>{number = 4, name = (null)}
2018-04-10 19:31:59.452501+0800 应用加载时间的优化[7570:1360370] 3---<NSThread: 0x600000075ac0>{number = 1, name = main}
2018-04-10 19:32:01.454278+0800 应用加载时间的优化[7570:1360370] 3---<NSThread: 0x600000075ac0>{number = 1, name = main}
2018-04-10 19:32:01.454655+0800 应用加载时间的优化[7570:1360370] group---end

总计:异步并发执行组一内职务和组二内职责,使用 dispatch_group_notify
监测以上两职分完毕后回主线程进入 block
内执行职责三。全程异步,不卡线程。

dispatch_group_wait

暂停当前线程(阻塞当前线程),等待指定的 group
中的职分履行到位后,才会往下继续执行。
代码:

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

log:

2018-04-10 19:49:33.996536+0800 应用加载时间的优化[7766:1383789] currentThread---<NSThread: 0x604000067f40>{number = 1, name = main}
2018-04-10 19:49:33.996684+0800 应用加载时间的优化[7766:1383789] group---begin
2018-04-10 19:49:36.000831+0800 应用加载时间的优化[7766:1383883] 2---<NSThread: 0x60400026ff00>{number = 3, name = (null)}
2018-04-10 19:49:36.000831+0800 应用加载时间的优化[7766:1383886] 1---<NSThread: 0x60000026ffc0>{number = 4, name = (null)}
2018-04-10 19:49:38.001697+0800 应用加载时间的优化[7766:1383883] 2---<NSThread: 0x60400026ff00>{number = 3, name = (null)}
2018-04-10 19:49:38.001987+0800 应用加载时间的优化[7766:1383886] 1---<NSThread: 0x60000026ffc0>{number = 4, name = (null)}
2018-04-10 19:49:38.003862+0800 应用加载时间的优化[7766:1383789] group---end

总结:使用
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);卡线程来等待 group
内容全方位实践完未来继续上边义务,已毕联机。如果去掉
dispatch_group_wait则不卡线程,第一时间执行 group---end,然后实施
group 中内容。

dispatch_group_enter、dispatch_group_leave

void dispatch_group_enter(dispatch_group_t group);
用来添加对应义务组中的未执行完成的职责数,执行三遍,未举办落成的职务数加1,当未履行完成职分数为0的时候,才会使
dispatch_group_wait 解除阻塞和dispatch_group_notify 的 block执行。
void dispatch_group_leave(dispatch_group_t group);
用以裁减义务组中的未举行完成的任务数,执行一次,未履行已毕的义务数减1,dispatch_group_enter
和 dispatch_group_leave 要合作,不然系统会认为 group
义务没有举行达成。
代码:

- (void)groupEnterAndLeave{
    NSLog(@"currentThread---%@",[NSThread currentThread]);// 打印当前线程
    NSLog(@"group---begin");
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 追加任务1
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);// 打印当前线程
        }
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 追加任务2
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);// 打印当前线程
        }
        dispatch_group_leave(group);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步操作都执行完毕后,回到主线程.
        for(int i =0; i <2; ++i) {
            [NSThread sleepForTimeInterval:2];// 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);// 打印当前线程
        }NSLog(@"group---end");
    });//
    // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
    //    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//
    NSLog(@"group---end"); 
}

log:

2018-04-10 20:05:44.440633+0800 应用加载时间的优化[7796:1394822] currentThread---<NSThread: 0x60000007f000>{number = 1, name = main}
2018-04-10 20:05:44.440802+0800 应用加载时间的优化[7796:1394822] group---begin
2018-04-10 20:05:44.441216+0800 应用加载时间的优化[7796:1394822] group---end
2018-04-10 20:05:46.443538+0800 应用加载时间的优化[7796:1394930] 2---<NSThread: 0x600000474440>{number = 3, name = (null)}
2018-04-10 20:05:46.443549+0800 应用加载时间的优化[7796:1394928] 1---<NSThread: 0x60400026a580>{number = 4, name = (null)}
2018-04-10 20:05:48.445104+0800 应用加载时间的优化[7796:1394930] 2---<NSThread: 0x600000474440>{number = 3, name = (null)}
2018-04-10 20:05:48.445101+0800 应用加载时间的优化[7796:1394928] 1---<NSThread: 0x60400026a580>{number = 4, name = (null)}
2018-04-10 20:05:50.445690+0800 应用加载时间的优化[7796:1394822] 3---<NSThread: 0x60000007f000>{number = 1, name = main}
2018-04-10 20:05:52.447481+0800 应用加载时间的优化[7796:1394822] 3---<NSThread: 0x60000007f000>{number = 1, name = main}
2018-04-10 20:05:52.447792+0800 应用加载时间的优化[7796:1394822] group---end

总结:等同于 dispatch_group_async

信号量:dispatch_semaphore

GCD 中的信号量是指 Dispatch
Semaphore,是怀有计数的信号。类似于过高速路收费站的栏杆。可以透过时,打开栏杆,不得以因而时,关闭栏杆。在Dispatch
Semaphore中,使用计数来成功那么些职能,计数为0时等待,不可通过。计数为1或当先1时,计数减1且不等待,可因此。

Dispatch Semaphore提供了多个函数。

  • dispatch_semaphore_create:创制一个Semaphore并开始化信号的总量
  • dispatch_semaphore_signal:发送一个信号,让信号总量加1
  • dispatch_semaphore_wait:可以使总信号量减1,当信号总量为0时就会平素等待(阻塞所在线程),否则就可以正常实施。

只顾:信号量的选择前提是:想精晓你必要处理哪个线程等待(阻塞),又要哪个线程继续执行,然后选拔信号量。

Dispatch Semaphore 在骨子里支出中第一用以:
1.维持线程同步,将异步执行职责转换为共同实施任务
2.担保线程安全,为线程加锁

Dispatch Semaphore 线程同步

俺们在付出中,会赶上这么的急需:异步执行耗时职分,并使用异步执行的结果开展部分很是的操作。换句话说,约等于,将将异步执行任务转换为联合实施职务。比如说:AFNetworking
中 AFURLSessionManager.m
里面的tasksForKeyPath:方法。通过引入信号量的方法,等待异步执行职分结果,获取到
tasks,然后再回去该 tasks。
代码:

- (void)semaphoreSync {
    NSLog(@"currentThread---%@",[NSThread currentThread]);// 打印当前线程
    NSLog(@"semaphore---begin");
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block int number = 0;
    dispatch_async(queue, ^{
        // 追加任务1
        [NSThread sleepForTimeInterval:2];// 模拟耗时操作
        NSLog(@"1---%@",[NSThread currentThread]);// 打印当前线程
        number = 100;
        dispatch_semaphore_signal(semaphore);
    });
    NSLog(@"main task continue");
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"semaphore---end,number = %zd",number);
}

log:

2018-04-10 21:58:57.974515+0800 应用加载时间的优化[8026:1443631] currentThread---<NSThread: 0x600000073900>{number = 1, name = main}
2018-04-10 21:58:57.974641+0800 应用加载时间的优化[8026:1443631] semaphore---begin
2018-04-10 21:58:57.974790+0800 应用加载时间的优化[8026:1443631] main task continue
2018-04-10 21:58:59.978823+0800 应用加载时间的优化[8026:1443709] 1---<NSThread: 0x604000269240>{number = 3, name = (null)}
2018-04-10 21:58:59.979016+0800 应用加载时间的优化[8026:1443631] semaphore---end,number = 100

小结:使用 semaphore 落成线程同步。可以在添加
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
以前落成主线程异步并发操作,在未来进行线程同步,等待子线程任务达成后执行后续操作。

线程安全和线程同步(为线程加锁)

线程安全:
若是你的代码所在的经过中有三个线程在同时运行,而这几个线程可能会同时运转那段代码。如果每趟运行结果和单线程运行的结果是千篇一律的,而且其余的变量的值也和预期的是均等的,就是线程安全的。
若每个线程中对全局变量、静态变量唯有读操作,而无写操作,一般的话,那一个全局变量是线程安全的;若有两个线程同时举办写操作(更改变量),一般都必要考虑线程同步,否则的话就可能影响线程安全。

线程同步:
可见道为线程 A 和 线程 B 一块合作,A 执行到一定水准时要凭借线程 B
的某部结果,于是停下来,示意 B 运行;B 依言执行,再将结果给 A;A
再持续操作。
举个大致例子就是:三个人在一道聊天。三个人不可能同时说道,防止听不清(操作争持)。等一个人说完(一个线程停止操作),另一个再说(另一个线程再开首操作)。

以售票为例:
非线程安全:(不利用 semaphore):
代码:

- (void)initTicketStatusNotSafe {
    NSLog(@"currentThread---%@",[NSThread currentThread]);// 打印当前线程
    NSLog(@"semaphore---begin");
    self.ticketSurplusCount = 50;
    // queue1 代表北京火车票售卖窗口
    dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
    // queue2 代表上海火车票售卖窗口
    dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
    __weak typeof(self) weakSelf =self;
    dispatch_async(queue1, ^{
        [weakSelf saleTicketNotSafe];
    });
    dispatch_async(queue2, ^{
        [weakSelf saleTicketNotSafe];
    });
}

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

Log:

2018-04-10 22:25:01.047999+0800 应用加载时间的优化[8218:1472786] currentThread---<NSThread: 0x604000064880>{number = 1, name = main}
2018-04-10 22:25:01.048150+0800 应用加载时间的优化[8218:1472786] semaphore---begin
2018-04-10 22:25:01.048378+0800 应用加载时间的优化[8218:1472878] 剩余票数:48 窗口:<NSThread: 0x6000002651c0>{number = 4, name = (null)}
2018-04-10 22:25:01.048387+0800 应用加载时间的优化[8218:1472892] 剩余票数:49 窗口:<NSThread: 0x604000272800>{number = 3, name = (null)}
2018-04-10 22:25:01.249347+0800 应用加载时间的优化[8218:1472878] 剩余票数:47 窗口:<NSThread: 0x6000002651c0>{number = 4, name = (null)}
2018-04-10 22:25:01.249354+0800 应用加载时间的优化[8218:1472892] 剩余票数:46 窗口:<NSThread: 0x604000272800>{number = 3, name = (null)}
2018-04-10 22:25:01.450904+0800 应用加载时间的优化[8218:1472878] 剩余票数:45 窗口:<NSThread: 0x6000002651c0>{number = 4, name = (null)}
2018-04-10 22:25:01.450904+0800 应用加载时间的优化[8218:1472892] 剩余票数:45 窗口:<NSThread: 0x604000272800>{number = 3, name = (null)}
2018-04-10 22:25:01.651358+0800 应用加载时间的优化[8218:1472892] 剩余票数:44 窗口:<NSThread: 0x604000272800>{number = 3, name = (null)}
2018-04-10 22:25:01.651358+0800 应用加载时间的优化[8218:1472878] 剩余票数:44 窗口:<NSThread: 0x6000002651c0>{number = 4, name = (null)}
.
.
.
2018-04-10 22:25:07.753135+0800 应用加载时间的优化
[8218:1472878] 剩余票数:2 窗口:<NSThread: 0x6000002651c0>{number = 4, name = (null)}
2018-04-10 22:25:07.753135+0800 应用加载时间的优化[8218:1472892] 剩余票数:2 窗口:<NSThread: 0x604000272800>{number = 3, name = (null)}
2018-04-10 22:25:07.958573+0800 应用加载时间的优化[8218:1472878] 剩余票数:1 窗口:<NSThread: 0x6000002651c0>{number = 4, name = (null)}
2018-04-10 22:25:07.958574+0800 应用加载时间的优化[8218:1472892] 剩余票数:0 窗口:<NSThread: 0x604000272800>{number = 3, name = (null)}

小结:剩余票数错乱,且再一次,存在多少个线程同时做客的场地,也就说存在同样张票卖一遍的动静。

线程安全:(使用 semaphore 加锁):
使用 semaphoreLock = dispatch_semaphore_create(1); 创建锁
使用 dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
加锁
使用 dispatch_semaphore_signal(semaphoreLock); 解锁
用加锁和平解决锁蕴含住可能会被四线程同时推行修改的代码。
代码:

static dispatch_semaphore_t semaphoreLock;

- (void)initTicketStatusSafe {
    NSLog(@"currentThread---%@",[NSThread currentThread]);// 打印当前线程
    NSLog(@"semaphore---begin");
    //value表示最大线程同时访问数,此处为1,更改会出问题
    semaphoreLock = dispatch_semaphore_create(1);
    self.ticketSurplusCount =50;
    // queue1 代表北京火车票售卖窗口
    dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
    // queue2 代表上海火车票售卖窗口
    dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
    __weak typeof(self) weakSelf = self;
    dispatch_async(queue1, ^{
        [weakSelf saleTicketSafe];
    });dispatch_async(queue2, ^{
        [weakSelf saleTicketSafe];
    });
}

- (void)saleTicketSafe {
    while(1) {
        // 相当于加锁
        dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
        if(self.ticketSurplusCount >0) {
            //如果还有票,继续售卖
            self.ticketSurplusCount--;NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@",self.ticketSurplusCount, [NSThread currentThread]]);
            [NSThread sleepForTimeInterval:0.2];
        }else{
            //如果已卖完,关闭售票窗口
            NSLog(@"所有火车票均已售完");
            // 相当于解锁
            dispatch_semaphore_signal(semaphoreLock);
            break;
        }// 相当于解锁
        dispatch_semaphore_signal(semaphoreLock);
    }
}

log:

2018-04-10 22:41:43.466847+0800 应用加载时间的优化[8424:1495856] currentThread---<NSThread: 0x60000007b700>{number = 1, name = main}
2018-04-10 22:41:43.466991+0800 应用加载时间的优化[8424:1495856] semaphore---begin
2018-04-10 22:41:43.467215+0800 应用加载时间的优化[8424:1495959] 剩余票数:49 窗口:<NSThread: 0x60400046e840>{number = 3, name = (null)}
2018-04-10 22:41:43.671865+0800 应用加载时间的优化[8424:1495961] 剩余票数:48 窗口:<NSThread: 0x600000270840>{number = 4, name = (null)}
2018-04-10 22:41:43.874748+0800 应用加载时间的优化[8424:1495959] 剩余票数:47 窗口:<NSThread: 0x60400046e840>{number = 3, name = (null)}
2018-04-10 22:41:44.078812+0800 应用加载时间的优化[8424:1495961] 剩余票数:46 窗口:<NSThread: 0x600000270840>{number = 4, name = (null)}
2018-04-10 22:41:44.280894+0800 应用加载时间的优化[8424:1495959] 剩余票数:45 窗口:<NSThread: 0x60400046e840>{number = 3, name = (null)}
2018-04-10 22:41:44.483298+0800 应用加载时间的优化[8424:1495961] 剩余票数:44 窗口:<NSThread: 0x600000270840>{number = 4, name = (null)}
2018-04-10 22:41:44.686228+0800 应用加载时间的优化[8424:1495959] 剩余票数:43 窗口:<NSThread: 0x60400046e840>{number = 3, name = (null)}
2018-04-10 22:41:44.890615+0800 应用加载时间的优化[8424:1495961] 剩余票数:42 窗口:<NSThread: 0x600000270840>{number = 4, name = (null)}
.
.
.
2018-04-10 22:41:53.450842+0800 应用加载时间的优化[8424:1495961] 剩余票数:0 窗口:<NSThread: 0x600000270840>{number = 4, name = (null)}
2018-04-10 22:41:53.656188+0800 应用加载时间的优化[8424:1495959] 所有火车票均已售完
2018-04-10 22:41:53.656569+0800 应用加载时间的优化[8424:1495961] 所有火车票均已售完

总括:可以看来,在考虑了线程安全的景况下,使用 dispatch_semaphore
机制之后,得到的票数是不利的,没有现身混乱的动静。我们也就解决了两个线程同步的问题。那也就是线程锁的重中之重

相关文章