旋即篇首要出口一下有跟实在相关的使实例,它是一个在线程池格局之底子及执行的产出任务

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. 在公用这一个格局的时节,你仍旧要衡量下总体的性能的,即便您行之任务时间比线程切换的年月还紧缺。那虽然小题大做了。
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)中,而是于起定义的行中。

dispatch_once

此可能大家还很之熟悉,这些于单例起头化的时候是苹果官方推荐的措施。这些函数可以保在应用程序中只执行指定的天职一样不佳。尽管以多线程的环境下举行,也得保证整个之安全。

    static id instance;
    static dispatch_once_t predicate;

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

    return instance;
}

及时个中的predicate必须是大局或者静态对象。在多线程下同时做客时,那一个情势以为线程同步等待,直到指定的block执行好。

GCD的运用手续

GCD的运有一定量步,第一步创制一个阵(串行队列或者相队列),第二步用任务在到等候队列中。

线程同步

当多线程中一个较根本的事物就是是线程同步的题材。假如多单线程只是针对某资源就是朗诵之进程,那么就无有这一个题材了。假设某线程对是资源要开展勾勒的操作,这是时刻虽然会见现出数未雷同的题目了。

线程间通信

于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}

小结:可以见到于另线程中优先实施任务,执行完毕了后头重临主线程执行主线程的相应操作。

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
    });
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

小结:所有任务都于主线程碰着,没有开发新的线程。任务由达到顶下,间隔两秒,依次执行。

以达到平等首被,大家重要谈了Dispatch
Queue
连带的情节。这篇紧要谈一下有的及骨子里相关的采用实例,Dispatch
Groups和Dispatch Semaphore。

队列

此间的行列指执行任务之等待队列,即用来存放弃务的排。队列是同等种独特之线性表,采用FIFO(先进先出)的标准,即新职责连续给插入到队的终极,而读取任务的时候总是打队列的满头开端读取。每读取一个职责,则从队列中自由一个任务。
GCD
中之天职有些许种:拧行队列连作班,两者都适合先称先有法,区别在于:执行顺序不同以及开启线程数不同。

  • 阴差阳错行队列(Serial Dispatch Queue)
    偏偏开一个线程,每一回唯有进行一个任务,执行完毕那一个职责连续下一个任务。
  • 连作班(Concurrent Dispatch Queue)
    好在差不六只线程中,让两只任务并发(同时)执行。注意:并发队列的面世效能唯有以异步(dispatch_async)函数下才有效

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来代表来个任务就好了。用此法肯定要留心得成双成对。

延时艺术: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 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);
    });

那样我们一致可管,线程的数额安全。

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
,然后在拉开之老五只线程中而且推行打定义的打印任务,然后以延迟三分钟后重新以打印第二破。

使用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创造出来的才行。

GCD的此外常用方法

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
内执行任务三。全程异步,不卡线程。

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,当前线程一向也主线程
总括:并发队列本身不克创制线程,而同步任务一样无开创线程。因此从来为主线程同步效果,没有兑现产出。

才实行同一糟代码: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 内部任务尽还唯有受执行同一破。

概念

Grand Central Dispatch(GCD)是 Apple
开发之一个多按编程的相比新的缓解办法。它根本用于优化应用程序以支撑多对处理器和另对如多处理连串。它是一个在线程池情势的根底及举办的起任务。

排与线程的涉:

主队列(main_queue)就是于主线程中之默认队列。并作班(global_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晚本顺序间隔两秒执行任务。不同之处在于没有开发新的线程,而是于主线程曰镪异步执行。
由此参照者所说之异步与产出的干,实际上异步使打定义之任务延后执行,整个线程仍是在主队列中串行执行于定义任务。而这力量,与异步错行队列效果等同,都是异步串行,根本区别在前者以任务在主线程的主队列中,而后者将任务在子线程的自定义队列中,也虽然是前者只出一个线程,后者多开发了一个线程。

信号量: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.保险线程安全,为线程加锁

GCD六种植组成下之情状

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);
以前实现主线程异步并发操作,在之后展开线程同步,等待子线程任务完毕后实施后续操作。

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
获队列

对串行队列,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);
高效迭代方法: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
中的平组方法,他暴发一个组的概念,可以将相关的天职由并至一个组内来实施,通过监听组内所有任务的举办情况来举办相应处理。

优点

  • GCD 可用以多按的互动运算
  • GCD 会自动利用再度多之 CPU 内核(比如双核、四查核)
  • GCD 会自动管理线程的生命周期(成立线程、调度任务、销毁线程)
  • 程序员只需要报告 GCD 想只要执行什么任务,不需要编制任何线程管理代码

任务与排

异步与产出的分:

异步,是乘执行顺序上,区别为同台的贯通,异步会延后行异步内的任务。而产出是负日达之,不同让串行的待,并发会同时施行并作班中之任务。

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

开创任务

GCD 提供了协同施行任务之创设方法 dispatch_sync 和异步执行任务创立方法
dispatch_async

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

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

虽用 GCD
只待两步,不过既然我们来少种队列(串行队列/并作班),二种植任务履行措施(同步执行/异步执行),那么我们虽有了季栽不同的咬合模式。实际上,刚才尚说了点儿种特别班:全局并作班、主队列。全局并作班能够当做普通连发队列来使用。但是主队列因为有点异样,所以大家便以基本上了个别种植组成情势。这样虽生出六种不同的做措施了。

  • 协施行 + 并作班
    不算。同步不可能开启新线程,不克落实产出。
  • 异步执行 + 并作班
    基本上线程的常用情势,异步并发,多线程同时展开多少个任务。
  • 一道施行 + 串行队列
    便串行,没有因而到差不多线程。
  • 异步执行 + 串行队列
    多线程的常用情势,异步串行,新建一个线程串行执行多独任务。
  • 合实施 + 主队列
    无效。新建任务急需等主队列任务完毕,主队列后续任务需要待新建任务,互相等待,代码卡死崩溃。
  • 异步执行 + 主队列
    接近异步串行,只是没有初开发线程,在主线程实现异步。
任务

职责就是实施操作的意,换句话说即是线程内推行的代码,在 GCD 中居
block 里。
尽任务的道来零星种植:一齐施行(sync)和异步执行(async),两者的根本分是:是不是等队列里之职责了跟是否拥有开启新线程的力量

  • 共实施(sync)
    同步添加任务交指定的系列中,在添加之天职执行完毕以前,会一向等待,直到队列之中的职责就未来再继续执行。只可以当时下线程中推行任务,不具开启新线程的力。
  • 异步执行(async)
    异步添加任务到指定的行中,它不会面召开任何等待,可以继续执行任务。可以当初的线程中进行任务,具备开启新线程的力量。

小心:异步执行(async)尽管有着开启新线程的力,但是并不一定开启新线程。这同任务所指定的行类型有关(下边会称)。

创制行

一般而言以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);
栅栏方法: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)}

总:由于是异步,所有任务还异步执行。参加了栅栏,所有异步任务先实施栅栏面前,再履行栅栏,最后执行栅栏后。

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,然后以被之初线程中串行执行打定义任务,依次间隔两秒。

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 中内容。

点滴只重要

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

dispatch_group_create

用以成立任务组
dispatch_group_t dispatch_group_create(void);

线程安全以及线程同步(为线程加锁)

线程安全:
若果你的代码所在的长河中生出差不多独线程在又运转,而这多少个线程可能会面以运行就段代码。如若每便运行结果跟单线程运行的结果是千篇一律的,而且其他的变量的值也跟预期的是均等的,就是线程安全的。
倘使每个线程中针对全局变量、静态变量只生读操作,而无论是写操作,一般的话,这多少个全局变量是线程安全之;若有差不两个线程同时举办写操作(更改变量),一般都需考虑线程同步,否则的语句虽可能影响线程安全。

线程同步:
可明白吧线程 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
机制后,得到的票数是毋庸置疑的,没有出现乱的气象。我们也即迎刃而解了五只线程同步的题目。这为就是线程锁的重点

相关文章