iOS多线程GCD简介(二)GCD

每当达到一致首被,我们任重而道远讲了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
机制后,得到的票数是毋庸置疑的,没有出现混乱的景象。我们呢就算缓解了差不多单线程同步的题材。这吗尽管是线程锁的要害

相关文章

admin

网站地图xml地图