GCD的使用总结

什么是多线程?


计算机在运行一段程序的时候,会把该程序的CPU命令列配置到内存中,然后按照顺序一个一个执行命令列,这样1个CPU执行的CPU命令列为一条无分叉路径就是线程。

而有多条这样的执行指令列的路径存在时即为多线程。

iOS实现多线程有4种方法:

  • pthreads
  • NSThread
  • GCD
  • NSOperation & NSOperationQueuef

 

这里我们主要讲GCD

 

一、Dispatch Queue和线程的关系

什么是Dispatch Queue?


如其名称,是执行处理的等待队列。当我们通过dispatch_async等函数把Block加入Dispatch Queue后,Dispatch Queue按照追加的顺序(FIFO)执行处理。

通过Dispatch Queue执行处理

Dispatch Queue的种类

 

  • Serial Dispatch Queue(串行队列) ——等待现在执行中处理结束再加入队列
  • Concurrent Dispatch Queue(并发队列) ——不等待现在执行中处理结束,直接加入队列

Serial Dispatch Queue

Concurrent Dispatch Queue

用代码说明:

 

Serial Dispatch Queue

dispatch_queue_t serial_queue = dispatch_queue_create("come.tanpei", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serial_queue, ^{
        NSLog(@"block 1");
    });
    dispatch_async(serial_queue, ^{
        NSLog(@"block 2");
    });
    dispatch_async(serial_queue, ^{
        NSLog(@"block 3");
    });
    dispatch_async(serial_queue, ^{
        NSLog(@"block 4");
    });

输出

2017-09-27 11:43:40.230126+0800 aegewgr[4327:1296458] block 1
2017-09-27 11:43:40.230335+0800 aegewgr[4327:1296458] block 2
2017-09-27 11:43:40.230461+0800 aegewgr[4327:1296458] block 3
2017-09-27 11:43:40.230548+0800 aegewgr[4327:1296458] block 4

这里Serial Dispatch Queue只会使用一个线程,因为它是串行队列,只会当一个处理执行完了才会将下一个任务交给线程处理。

 

Concurrent Dispatch Queue

dispatch_queue_t concurrent_queue = dispatch_queue_create("come.tanpei", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(concurrent_queue, ^{
        NSLog(@"block 1");
    });
    dispatch_async(concurrent_queue, ^{
        NSLog(@"block 2");
    });
    dispatch_async(concurrent_queue, ^{
        NSLog(@"block 3");
    });
    dispatch_async(concurrent_queue, ^{
        NSLog(@"block 4");
    });

输出如下:

2017-09-27 11:45:09.057505+0800 aegewgr[4349:1304484] block 3
2017-09-27 11:45:09.057505+0800 aegewgr[4349:1304483] block 1
2017-09-27 11:45:09.057522+0800 aegewgr[4349:1304486] block 4
2017-09-27 11:45:09.057505+0800 aegewgr[4349:1304485] block 2 

block的执行完成

是随机的,因为他们虽然是按顺序把任务提交给线程,但是因为不需要等待前一个任务执行,所以几乎是同时交给线程处理的。所以这里会使用多个线程,而具体线程数的多少由XNU内核决定。

 

Concurrent Dispatch Queue的执行

二、Dispatch Queue的使用

1、获取队列

在使用Dispatch Queue的时候我们可以通过dispatch_queue_create函数创建队列,也可以获取系统给我们提供的队列。系统给我们提供了两种队列

系统提供的Dispatch Queue

 

2、同步与异步
  • dispatch_async表示异步:将指定的Block”非同步“加入Dispatch Queue,不做任何等待

异步执行

  • dispatch_sync表示同步:将指定的Block”同步“的加入Dispatch Queue,在Block结束之前,dispatch_sync函数会一直等待

同步执行

3、死锁

 

由于dispatch_sync会等待Block执行结束才会继续往下执行,所以会产生死锁的情况

 

我们直接在主线程中同步加入一个Blcok:

dispatch_queue_t main_queue = dispatch_get_main_queue();
    dispatch_sync(main_queue, ^{
        NSLog(@"main queue");
    });
    NSLog(@"go on");

无任何输出,程序直接卡死了。这就是造成了死锁

因为该源代码在main_queue(主线程)中加入一个加入一个指定的Block,并等待其执行结束。而由于main_queue是一个串行队列,它要等当前线程中的任务处理完后才会把队列中的任务提交到主线程,而主线程又在等待这段代码执行,所以造成了相互等待,就产生了死锁。(而并发队列不会产生死锁)
如:

dispatch_queue_t global_queue = dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT);
    dispatch_sync(global_queue, ^{
        NSLog(@"global_queue out");
        dispatch_sync(global_queue, ^{
            NSLog(@"global_queue in");
        });
    });

输出如下:

2017-09-27 16:11:56.332317+0800 aegewgr[4723:1590202] global_queue out
2017-09-27 16:11:56.332446+0800 aegewgr[4723:1590202] global_queue in

所以产生死锁的话一般都是在串行队列中并且是在一个线程中同步往这个线程提交一个Block。

 

4、Dispatch Group(派发分组)

Dispatch Group是GCD的一项特性,能够把任务分组。调用者在这组任务执行完毕后会得到通知,并做相应的处理。

创建:dispatch_group_t group = dispatch_group_create();
同样的,它也有
dispatch_group_async(dispatch_group_t _Nonnull group, dispatch_queue_t _Nonnull queue, <#^(void)block#>)dispatch_sync函数没有什么区别,它只是多了一个dispatch_group_t参数,来把任务进行分组。

还有一种方法能把任务加入dispatch_group,那就是下面这对情侣:

    dispatch_group_enter(dispatch_group_t group);
    dispatch_group_leave(dispatch_group_t group);

记住,这对情侣一定要成对出现,dispatch_group_enter就是标志下面的代码要加入dispatch_group。dispatch_group_leave就是表示加入dispatch_group的代码结束。也就是说dispatch_group_enter和dispatch_group_leave之间的代码就是加入dispatch_group中的。

说了这么多,把一个队列加入dispatch_group后有什么用呢?主要就是一组相似的操作结束后,你可以通过dispatch_block_notify(dispatch_block_t block, dispatch_queue_t queue, dispatch_block_t notification_block)函数来获得通知,并进行相应的处理。Block参数就是你要添加的处理。

当然,如果你想设置一个等待时间,可以使用dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout)函数,该函数设置了一个等待时间也就是说程序要一直阻塞当前线程直到group中的任务执行完毕或者超过等待时间,才会继续往下执行。

dispatch_block_notify函数不会阻塞当前线程,它只是指定了一个group任务执行完后的回调。

需要举个栗子吗?
好吧,还是举个栗子吧。

dispatch_queue_t global_queue = dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT);
    dispatch_group_t group = dispatch_group_create();
    ;
    dispatch_group_async(group, global_queue, ^{
        NSLog(@"task 1");
    });
    dispatch_group_async(group, global_queue, ^{
        NSLog(@"task 2");
    });
    dispatch_group_async(group, global_queue, ^{
        NSLog(@"task 3");
    });
    dispatch_group_notify(group, global_queue, ^{
        NSLog(@"notify");
    });
    NSLog(@"other task");

输出如下:

2017-09-27 17:06:37.795564+0800 aegewgr[4983:1713915] other task
2017-09-27 17:06:37.795571+0800 aegewgr[4983:1714182] task 3
2017-09-27 17:06:37.795578+0800 aegewgr[4983:1714181] task 1
2017-09-27 17:06:37.795578+0800 aegewgr[4983:1714183] task 2
2017-09-27 17:06:37.795813+0800 aegewgr[4983:1714183] notify

可以看到dispatch_group_notify并没有阻塞当前线程,而且它提交的Block一定是当group中的所有任务执行完后才会执行。另外,这里的queue可以不是一个queue,你可以使用任意其它queue,不过最好是并发队列,如果是串行队列,任务会按顺序一个一个执行,那使用group的意义就不大了。

看看dispatch_group_wait

dispatch_queue_t global_queue = dispatch_get_global_queue(0, DISPATCH_QUEUE_PRIORITY_DEFAULT);
    dispatch_group_t group = dispatch_group_create();
    ;
    dispatch_group_async(group, global_queue, ^{
        NSLog(@"task 1");
    });
    dispatch_group_async(group, global_queue, ^{
        NSLog(@"task 2");
    });
    dispatch_group_async(group, global_queue, ^{
        NSLog(@"task 3");
    });
    dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1));
    NSLog(@"other task");

输出如下:

2017-09-27 17:10:10.968133+0800 aegewgr[5002:1726567] task 2
2017-09-27 17:10:10.968133+0800 aegewgr[5002:1726568] task 1
2017-09-27 17:10:10.968140+0800 aegewgr[5002:1726569] task 3
2017-09-27 17:10:10.968133+0800 aegewgr[5002:1726443] other task

可以看到dispatch_group_wait函数阻塞了当前线程,只有当group中的所有任务执行完后线程才会继续往下执行。

5 、其它相关函数
  • dispatch_barrier_async和dispatch_barrier_sync(栅栏)

这两个函数的作用差不多,都是把它前面和它后面的函数分隔开。使它前面的任务先执行,再执行它添加的任务,最后执行它后面的任务。

那么它们有什么区别呢?

当然从名字就能看出来,就是提交任务的方式不同,一个是同步一个是异步,同步和异步的区别前面有解释,如果忘了的话,可以再回去看看。

  • Dispatch Semaphore(信号量)

信号量其实就是用来保证访问资源的线程数,当信号量大于等于1时,资源可以访问,否则无法访问资源,直到其它线程释放资源。

这里主要有三个函数:

dispatch_semaphore_t dispatch_semaphore_create(long value);  //创建一个dispatch_semaphore_t,value为初始信号量
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);   //信号量-1
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);   //信号量+1 

怎么用呢?

还是举个栗子吧:

假如有两个资源,但是同时有三个线程想要访问,就可以使用信号量进行控制:

//crate的value表示,最多几个资源可访问
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //任务1
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 1");
        sleep(1);
        NSLog(@"complete task 1");
        dispatch_semaphore_signal(semaphore);
    });
    //任务2
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 2");
        sleep(1);
        NSLog(@"complete task 2");
        dispatch_semaphore_signal(semaphore);
    });
    //任务3
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"run task 3");
        sleep(1);
        NSLog(@"complete task 3");
        dispatch_semaphore_signal(semaphore);
    });

输出如下:

2017-09-27 18:04:27.590428+0800 aegewgr[5149:1860224] run task 1
2017-09-27 18:04:27.590428+0800 aegewgr[5149:1860221] run task 2
2017-09-27 18:04:28.591086+0800 aegewgr[5149:1860224] complete task 1
2017-09-27 18:04:28.591086+0800 aegewgr[5149:1860221] complete task 2
2017-09-27 18:04:28.591386+0800 aegewgr[5149:1860219] run task 3
2017-09-27 18:04:29.591845+0800 aegewgr[5149:1860219] complete task 3

假如把信号量设置为3呢?

 dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);

输出如下:

2017-09-27 18:08:37.535634+0800 aegewgr[5169:1873722] run task 2
2017-09-27 18:08:37.535637+0800 aegewgr[5169:1873721] run task 1
2017-09-27 18:08:37.535636+0800 aegewgr[5169:1873723] run task 3
2017-09-27 18:08:38.539585+0800 aegewgr[5169:1873723] complete task 3
2017-09-27 18:08:38.539585+0800 aegewgr[5169:1873721] complete task 1
2017-09-27 18:08:38.539585+0800 aegewgr[5169:1873722] complete task 2
  • dispatch_once
    此函数在我们创建单例的时候经常会用到,就是可以保证在应用程序执行中该函数只执行一次。即使在多线程环境也,也可以保证百分百的安全。

来源:https://mp.weixin.qq.com/s/CPpy2zqugyPwNzGLOonUGw

原文地址:https://www.cnblogs.com/hualuoshuijia/p/11378689.html

时间: 2024-10-08 23:10:09

GCD的使用总结的相关文章

dutacm.club 1094: 等差区间(RMQ区间最大、最小值,区间GCD)

1094: 等差区间 Time Limit:5000/3000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)Total Submissions:655   Accepted:54 [Submit][Status][Discuss] Description 已知一个长度为 n 的数组 a[1],a[2],-,a[n],我们进行 q 次询问,每次询问区间 a[l],a[l+1],-,a[r?1],a[r] ,数字从小到大

iOS GCD中级篇 - dispatch_group的理解及使用

前文我们讲了GCD基础篇,以及同步.异步,并发.并行几个概率的理解. 参考链接: iOS GCD基础篇 - 同步.异步,并发.并行的理解 现在讲一下dispatch_group的概念以及几种场景下的使用 1.关于dispatch_group 把一组任务提交到队列中,这些队列可以不相关,然后监听这组任务完成的事件. 最常见的几个方法: 1.dispatch_group_create创建一个调度任务组 2.dispatch_group_async 把一个任务异步提交到任务组里 3.dispatch_

转 GCD

GCD 深入理解:第一部分 本文翻译自 http://www.raywenderlich.com/60749/grand-central-dispatch-in-depth-part-1 原作者:Derek Selander 译者:@nixzhu 虽然 GCD 已经出现过一段时间了,但不是每个人都明了其主要内容.这是可以理解的:并发一直很棘手,而 GCD 是基于 C 的 API ,它们就像一组尖锐的棱角戳进 Objective-C 的平滑世界.我们将分两个部分的教程来深入学习 GCD . 在这两

GCD多线程死锁总结

// //  ViewController.m //  多线程 // // #import "ViewController.h" @interface ViewController () @end @implementation ViewController /* >1 队列和线程的区别: 队列:是管理线程的,相当于线程池,能管理线程什么时候执行. 队列分为串行队列和并行队列 串行队列:队列中的线程按顺序执行(不会同时执行) 并行队列:队列中的线程会并发执行,可能会有一个疑问,队

GCD 常用操作

GCD多线程操作 1)用得最多的操作 //获取全局队列 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //异步执行 dispatch_async(queue, ^{ //下载图片 dispatch_async(dispatch_get_main_queue(), ^{ //回到主线程更新UI }); }); ? 2)延时执行 1-调用NSObject方法 [Self

GCD

Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispatch_get_main_queue获取. /*! * @function dispatch_get_main_queue * * @abstract * Returns the default queue that is bound to the main thread. * * @discussi

iOS开发——多线程OC篇&amp;GCD实用总结

GCD实用总结 图片下载 注:iOS开发中常见GCD的实用也就这些了, 先来看看之前我们经常使用的方式: 1 static NSOperationQueue * queue; 2 3 - (IBAction)someClick:(id)sender { 4 self.indicator.hidden = NO; 5 [self.indicator startAnimating]; 6 queue = [[NSOperationQueue alloc] init]; 7 NSInvocationO

猫猫学iOS(五十二)多线程网络之GCD下单例设计模式

猫猫分享,必须精品 原创文章,欢迎转载.转载请注明:翟乃玉的博客 地址:http://blog.csdn.net/u013357243?viewmode=contents 单例模式 1:单例模式的作用 可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问 从而方便地控制了实例个数,并节约系统资源 单例模式的使用场合 在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次) 简单来说,就是我弄了一个工具类,他就有一份,比如我设计了一个音乐播放器NYPlayer,这个播放器类我

iOS 开发之多线程之GCD

1.GCD(Grand Centrol Dispath) 并行:宏观以及微观都是两个人再拿着两把铁锹在挖坑,一小时挖两个大坑 并发:宏观上是感觉他们都在挖坑,微观是他们是在使用一把铁锹挖坑,一小时后他们挖了两个小坑. 总结:就单个cpu来说,大部分进程是并发进行的,就是一把铁锹,你一下我一下,只是间隔时间较短,用户感觉不到而已. 应用: GCD包括: (1)实际使用中 //dispatch_get_global_queue(0, 0)第一个0是优先级,第二个保留字段 dispatch_async

hdu 4497 GCD and LCM

GCD and LCM Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 1092    Accepted Submission(s): 512 Problem Description Given two positive integers G and L, could you tell me how many solutions of (