能够不用多线程,我们根本讲了

在上一篇中,大家任重(英文名:)而道远讲了Dispatch
Queue
相关的始末。那篇主要讲一下有的和实际相关的运用实例,Dispatch
Groups和Dispatch Semaphore。


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

1.何为多线程

  • 三十二线程是一个应用程序同时执行八个职务,线程是实施顺序的最大旨单元,他有温馨的栈和寄
    存器,线程既是一个CPU执行的无分叉的命令列。多少个职务即表示有多组栈和寄存器中的值要求持续地被备份、替换。由此十二线程本身会带来功能上的损失,不考虑其余任何因素和技艺二十四线程是会稳中有降效能的。

  • 确切的话,在拍卖并发任务时,多线程不仅不可能升高功能,反而还会下滑程序效能

dispatch_once

以此或许大家都特其余熟练,那几个在单例伊始化的时候是苹果官方推荐的法门。那么些函数可以有限辅助在应用程序中只举行指定的天职一回。即便在二十四线程的条件下实施,也足以保险所有的延安。

    static id instance;
    static dispatch_once_t predicate;

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

    return instance;
}

那其中的predicate无法不是大局或者静态对象。在多线程下同时做客时,那一个艺术将被线程同步等待,直到指定的block执行到位。

2. 涌出和相互

  • 并发指的是一种情景,一种日常出现,无可防止的气象,它描述的是“八个任务同时暴发,必要被处理”
    这一情景,它的基点在与暴发。

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来表示有个职责现已做到了。用这一个格局肯定要留心必须成双成对。

譬如景点开放了四个检票窗口,同一时间内服务七个乘客,那种情景可以领略为相互。

*大家平日挂在嘴边的“多线程”,正是利用了交互技术,从而增强了执行功效。因为有多个线程,所以统计机的四个CPU可以同时工作,同时处理分裂线程内的授命。

现身是一种景况,面对这一气象,我们率先创建五个线程,真正加快程序运行速度的,是互相技术。也就是让八个CPU同时工作。而三八线程,是为了让多少个CPU同时工作成为可能

线程同步

在四线程中一个比较关键的事物就是线程同步的题材。假如多少个线程只是对某个资源只是读的进程,那么就不存在那一个问题了。借使某个线程对这几个资源需要举办写的操作,那那几个时候就会冒出数量不均等的题目了。

3.总结

于是乎大家得以得出结论,在需求同时处理IO和UI的景况下,真正起效果的是异步,而不是八线程,可以不用三八线程,然而必须用异步

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

2.常用的二十四线程方案 NSThread、GCD、NSOperation & NSOperationQueue

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

如此大家一致可以有限支撑,线程的数据安全。

1.NSThread

NSThread是Objective-C的根基框架的一有些,并为开发者提供一种方法来创制和保管线程

特点:

  • 基于OC的言语API,面向对象操作,可以一贯操控线程对象,极度直观和便民。
  • 线程的生命周期由程序员管理,偶尔使用(多用来debug)
示例 NSThread:
- (void)creatThread{
    NSThread *thread =[[NSThread alloc]initWithTarget:self selector:@selector(runThead) object:nil];
    [thread start];
     /* 创建并且自动启动 */
    [NSThread detachNewThreadSelector:@selector(runThead2) toTarget:self withObject:nil];
     /* 使用 NSObject 的方法创建并自动启动 */
    [self performSelectorInBackground:@selector(runThead3) withObject:nil];
}

- (void)runThead{
    NSLog(@"创建线程1:%@", [NSThread currentThread]);
}
- (void)runThead2{
     NSLog(@"自动启动线程2%@", [NSThread currentThread]);
}
- (void)runThead3{
     NSLog(@"自动启动线程3%@", [NSThread currentThread]);
}

2.GCD

GCD为Grand Central Dispatch的缩写
它是苹果为多核的相互运算提议的缓解方案,所以会自行合理地利用更加多的CPU内核(比如双核、四核),最重点的是它会活动管理线程的生命周期(创造线程、调度任务、销毁线程),完全不必要大家管理,大家只需求告诉干什么就行。同时它使用的也是
c语言,但是由于使用了
Block(斯威·夫特(S·wift)里叫做闭包),使得应用起来更为便于,而且灵活

1. 职务和队列

在 GCD 中,加入了五个要命关键的概念: 职务 和 队列。

职务:即操作,你想要干什么,说白了就是一段代码,在 GCD 中就是一个
Block,所以添加任务越发造福。职分有三种实施措施: 同步执行 和
异步执行,他们之间的界别是 是或不是会创建新的线程。

协办和异步

<strong>同步(sync)</strong> 和
<strong>异步(async)</strong>的主要差别在于会不会卡住当前线程,知道block中的职分履行完成!

<strong>同步(sync)</strong>操作,他会堵塞当前线程并等候block中的义务履行完结,然后当前线程才会一连往下运作。

<strong>异步(async)</strong>当前线程会从来往下执行,它不会堵塞当前线程

同步 异步
主队列 在主线程执行 在主线程执行
串行队列 在当前线程执行 新建线程执行
并发队列 在当前线程执行 新建线程执行
dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

理清函数

在创造 dispatch queue 之后,可以附加一个 finalizer 函数,在 queue
被灭绝从前实施自定义的清理操作。使用dispatch_set_finalizer_f 函数为
queue 指定一个清理函数,当 queue 的引用计数到达 0 时(ARC
下即便您看不到了但是原理依旧那样),且唯有上下文指针不为 NULL
时才会调用这么些清理函数。

累加单个职务到Queue

你可以异步或联手地抬高一个职责到
Queue(异步与共同的区分就是是或不是封堵当前线程)。

尽可能地选取 <strong>dispatch_async 或 dispatch_async_f
</strong>函数<font color =red>异步</font>地 dispatch
职分。因为加上职分到 Queue
中时,不能确定这么些代码几时可以实践。因而异步地抬高 block
或函数,可以让你当时调度那么些代码的举办,然后调用线程可以继续去做其余工作

2. GCD的死锁问题

www.888000ff.com,在接纳GCD的进度中,借使向当前串行队列中一道派发一个任务,就会招致死锁

纯属不要在任务中调用 dispatch_sync 或 dispatch_sync_f 函数,并联合
dispatch 新义务到近期正在履行的 queue。对于串行 queue
那点专门重大,因为这么做肯定会促成死锁;而并发 queue
也相应幸免那样做,否则即便并发 queue
不去引起运行时不当,不过被锁的有些永远不会被实施到

  • 大家了解dispatch_sync表示同步的施行职分,也就是说执行dispatch_sync后,当前队列会卡住。而dispatch_sync中的block假如要在当下队列中履行,就得拭目以待眼前队列程执行到位
  • 在地点那一个事例中,主队列在执行dispatch_sync,随后队列中新增一个职分block。因为主队列是同步队列,所以block要等dispatch_sync执行完才能履行,不过dispatch_sync是共同派发,要等block执行完才算是停止。在主队列中的四个任务相互等待,导致了死锁
缓解方案
  • 实际上在常常状态下大家不用要用dispatch_sync,因为dispatch_async可以更好的选用CPU,提高程序运行速度。
  • 除非当大家须求保险队列中的职务必须逐项执行时,才考虑采纳dispatch_sync。在使用dispatch_sync的时候应该分析当前处在哪个队列,以及义务会付给到哪些队列。

  • 串行队列

 dispatch_queue_t queue = dispatch_queue_create("", NULL);
  dispatch_queue_t queue = dispatch_queue_create("test.Lision.testQueue", DISPATCH_QUEUE_SERIAL);
  • 相互队列

 dispatch_queue_t queue = dispatch_queue_create("test.Lision.testQueue", DISPATCH_QUEUE_CONCURRENT);

- (void)lockGCD{
    dispatch_queue_t myCustomQueue =dispatch_queue_create("test.myCustomQueue", NULL);
    /* 异步执行 */
    dispatch_async(myCustomQueue, ^{
        NSLog(@"Do some work here.\n");
    });
    NSLog(@"The first block may or may not have run.\n");
     /* 同步执行 */
    dispatch_sync(myCustomQueue, ^{
        NSLog(@"同步非主队列");
    });
    // 由于上面的操作是同步操作会阻塞当前线程,所以执行下面的打印时上面的操作肯定是已经完毕的
    NSLog(@"同步非主队列Both blocks have completed.\n");
     /* 如国是当前线程同步执行则会引发死锁 */
    dispatch_queue_t myMainQueue =dispatch_get_main_queue();
    dispatch_sync(myMainQueue, ^{
          NSLog(@"同步主队列");
    });
     NSLog(@"同步主队列Both blocks have completed.\n");
}
3. Dispatch Queue 和线程安全性

行使 Dispatch Queue 完毕应用出现时,也亟需小心线程安全性

  • Dispatch queue
    本身是线程安全的。换句话说,你能够在利用的任意线程中提交职务到
    dispatch queue,不须求运用锁或其余同步机制
  • 并非在实践职责代码中调用 dispatch_sync 函数调度相同的
    queue,那样做会死锁这些 queue。若是您须求 dispatch 到当前
    queue,需求动用 dispatch_async 函数异步调度
  • 避免在交付到 dispatch queue
    的职责中取得锁,即便在职务中采纳锁是平安的,但在伸手锁时,倘若锁不可用,可能会完全堵塞串行
    queue。类似的,并发 queue
    等待锁也可能阻挡其他职责的推行。要是代码必要联合,就选取串行
    dispatch queue
  • 尽管可以取得运行职责的底层线程的信息,最好不用那样做
4.Dispatch Semaphore(信号量)

好像于传统的
semaphore(信号量),可是越来越便捷。唯有当调用线程由于信号量不可用,需求阻塞时,Dispatch
semaphore 才会去调用内核。若是信号量可用,就不会与根本举办交互
应用信号量可以完成对有限资源的访问控制

运用 Dispatch Semaphore 的长河如下:

  1. 使用 dispatch_semaphore_create 函数创立 semaphore,指定正数值表示
    资源的可用数量
  2. 在各类任务中,调用 dispatch_semaphore_wait 来等待 semaphore
  3. 当上边调用重临时,获得资源并起先工作
  4. 行使完资源后,调用 dispatch_semaphore_signal 函数释放和 signal
    那几个 semaphore

#pragma mark GCD dispatch_semaphore_t信号量
- (void)semaphoreTest{
   dispatch_group_t group = dispatch_group_create();
   dispatch_queue_t mySemaphoreQueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   // 创建信号 3标识资源可用数量 
   //  某个线程执行到这里,如果信号量值为1,那么wait方法返回1,开始执行接下来的操作。
    与此同时,因为信号量变为0,其它执行到这里的线程都必须等待 */
   dispatch_semaphore_t mySemaphore =dispatch_semaphore_create(3);

   // 等待一个可用的文件描述符 
  <!--  执行了wait方法后,信号量的值变成了0。可以进行接下来的操作。
    这时候其它线程都得等待wait方法返回。
    可以对array修改的线程在任意时刻都只有一个,可以安全的修改array-->
   for (int i =0; i<30; i++) {
       dispatch_semaphore_wait(mySemaphore, DISPATCH_TIME_FOREVER);
       dispatch_async(mySemaphoreQueue, ^{
           [NSThread sleepForTimeInterval:5];//模拟代码执行时间
           NSLog(@"等待----执行工作 %d",i);
           /*
            排他操作执行结束,记得要调用signal方法,把信号量的值加1。
            这样,如果有别的线程在等待wait函数返回,就由最先等待的线程执行。  */
           dispatch_semaphore_signal(mySemaphore);
       });

   }
   dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}

5.dispatch_group_notify

dispatch_group 执行完一组异步操作后方可透过
dispatch_group_notify来通告主线程,反馈音信给用户

- (void)group_notify_Test{
   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   dispatch_group_t group =dispatch_group_create();
   // 把 queue 加入到 group
   dispatch_group_async(group, queue, ^{
       dispatch_async(queue, ^{
            [NSThread sleepForTimeInterval:5];//模拟代码执行时间
            [NSThread sleepForTimeInterval:15];//模拟代码执行时间
            [NSThread sleepForTimeInterval:2];//模拟代码执行时间
           NSLog(@"模拟代码完成");
       });
   });
   dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
       // 从主线程上执行 UI 界面更新
       NSLog(@"从主线程上执行 UI 界面更新");
   });
}

<font color =red>iOS AFNetworking 四个网络请求顺序重回数据</font>

应用信号量机制顺序打印

dispatch_group_t group=dispatch_group_create();

    dispatch_semaphore_t semaphore=dispatch_semaphore_create(1);

    dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    for (int i=0; i<100; i++) {

        //信号量减1,如果同时开启1个以上的线程,则信号量小于等于0,此时就会阻塞该线程。

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

        dispatch_group_async(group, queue, ^{

            NSLog(@"test %d",i);

        //每个线程执行减1后通过信号量通知加1,这样始终保持线程在10个之内

        dispatch_semaphore_signal(semaphore);

        });

    }

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

3. NSOperation

描述
NSInvocationOperation 可以直接使用的类,基于应用的一个对象和 selector 来创建 operation object。如果你已经有现有的方法来执行需要的任务,就可以使用这个类
NSBlockOperation 可以直接使用的类,用来并发地执行一个或多个 block 对象。operation object 使用“组”的语义来执行多个 block 对象,所有相关的 block 都执行完成之后,operation object 才算完成。
NSOperation 基类,用来自定义子类 operation object。继承 NSOperation 可以完全控制 operation object 的实现,包括修改操作执行和状态报告的方式
-(void)InvocationOperation_Test{
    NSInvocationOperation *invocation =[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationTest) object:nil];
    [invocation start];
}

- (void)blockOperation_Test{
   NSBlockOperation *blockOperation =[NSBlockOperation blockOperationWithBlock:^{
       NSLog(@"do code");
   }];
}

- (void)operationTest{

}

相关文章