多线程--GCD的基本用法

---恢复内容开始---

GCD

GCD的优势

  GCD是苹果公司为多核的并行运算提出的解决方案

  GCD会自动利用更多的CPU内核(比如双核、四核)

  GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
GCD中的核心概念
  任务  --存放执行的事情
  队列 --存放任务
 
  GCD的执行: 
  • GCD会自动将队列中的任务取出,放到对应的线程中执行
  • 任务的取出遵循队列的FIFO原则:先进先出,后进后出 
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
/********************************创建串行queue队列********************************/
DISPATCH_QUEUE_SERIAL这里换成NULL也是创建串行队列
dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
/********************************第一次向queue添加任务********************************/
        for (int i = 0; i<3; i++) {
            NSLog(@"A------%@", [NSThread currentThread]);
        }
        for (int i = 0; i<3; i++) {
            NSLog(@"B------%@", [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
/********************************第二次向queue添加任务********************************/
        for (int i = 0; i<3; i++) {
            NSLog(@"C------%@", [NSThread currentThread]);
        }
        for (int i = 0; i<3; i++) {
            NSLog(@"D------%@", [NSThread currentThread]);
        }
    });
    
    dispatch_release(queue);
    
    NSLog(@"E--------%@", [NSThread currentThread]);
}

 输出结果:

2016-03-29 21:28:50.991 test1[1021:62256] E--------<NSThread: 0x7fcaa0601a00>{number = 1, name = main}
2016-03-29 21:28:50.991 test1[1021:62291] A------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.992 test1[1021:62291] A------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.992 test1[1021:62291] A------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.992 test1[1021:62291] B------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] B------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] B------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] C------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] C------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.993 test1[1021:62291] C------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.994 test1[1021:62291] D------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.994 test1[1021:62291] D------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}
2016-03-29 21:28:50.994 test1[1021:62291] D------<NSThread: 0x7fcaa2201900>{number = 2, name = (null)}

 

上面两句话结合代码分析:就是当我们把我们要做的事情(任务)添加进一个队列中,可能我们分多次添加,上面的代码中我是跟两次在queue队列中添加任务,当GCD把queue队列中的任务取出放到对应的线程中执行的时候, 它会遵守FIFO(先进先出的原则),先执行第一次添加的任务(输出A B),然后执行第二次添加的任务(输出C D),为什么输出结果中会首先输出E呢? 因为这里执行的异步函数执行任务,不会阻塞线程 (下面会讲异步函数) 串行队列 + 异步执行函数会开启一条子线程

注意:上面的案例是在MRC中,所以通过creat创建出来的队列,需要release! 

GCD队列的创建以及类型   
/********************************创建串行queue队列********************************/
    dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);

 

/********************************创建并发queue队列********************************/
    dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);

 

 串行队列:队列中的任务执行的方式:任务一个接着一个的执行(一个执行完毕以后,在执行下一个任务)

并发队列:当GCD取出队列中的任务的时候,执行的方式:如果是异步执行的方式的执行下,自动开启多个线程同时执行任务)

将上面的案例中的队列更改成并发队列,我们分析下结果:

/********************************创建并发queue队列********************************/
    dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        /********************************第一次向queue添加任务********************************/
        for (int i = 0; i<3; i++) {
            NSLog(@"A------%@", [NSThread currentThread]);
        }
        for (int i = 0; i<3; i++) {
            NSLog(@"B------%@", [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        /********************************第二次向queue添加任务********************************/
        for (int i = 0; i<3; i++) {
            NSLog(@"C------%@", [NSThread currentThread]);
        }
        for (int i = 0; i<3; i++) {
            NSLog(@"D------%@", [NSThread currentThread]);
        }
    });
    
    dispatch_release(queue);
    
    NSLog(@"E--------%@", [NSThread currentThread]);

 输出结果:

2016-03-29 21:48:37.056 test1[1043:68365] E--------<NSThread: 0x7fd838402780>{number = 1, name = main}
2016-03-29 21:48:37.056 test1[1043:68403] C------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)}
2016-03-29 21:48:37.056 test1[1043:68404] A------<NSThread: 0x7fd83843b470>{number = 3, name = (null)}
2016-03-29 21:48:37.056 test1[1043:68404] A------<NSThread: 0x7fd83843b470>{number = 3, name = (null)}
2016-03-29 21:48:37.056 test1[1043:68403] C------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)}
2016-03-29 21:48:37.057 test1[1043:68404] A------<NSThread: 0x7fd83843b470>{number = 3, name = (null)}
2016-03-29 21:48:37.057 test1[1043:68403] C------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)}
2016-03-29 21:48:37.057 test1[1043:68404] B------<NSThread: 0x7fd83843b470>{number = 3, name = (null)}
2016-03-29 21:48:37.057 test1[1043:68403] D------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)}
2016-03-29 21:48:37.057 test1[1043:68404] B------<NSThread: 0x7fd83843b470>{number = 3, name = (null)}
2016-03-29 21:48:37.057 test1[1043:68403] D------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)}
2016-03-29 21:48:37.058 test1[1043:68404] B------<NSThread: 0x7fd83843b470>{number = 3, name = (null)}
2016-03-29 21:48:37.058 test1[1043:68403] D------<NSThread: 0x7fd838601ed0>{number = 2, name = (null)}

结果分析:第一行依然输出的是E;因为我们执行任务的方式是异步函数,所以不会堵塞线程,输出的是E,但是这次由于我们创建的队列是并发的,当GCD从队列中取出任务的时候,会自动创建多个线程(根据CPU资源创建),不仅从输出的结果可以看出,创建多条线程,并且执行的任务的时候没有顺序的,说明是并发执行任务的

下面我们看一下同步函数执行任务 + 串行队列 或者 并发队列是什么结果呢

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    /********************************创建异步queue队列********************************/
    dispatch_queue_t queue = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        /********************************第一次向queue添加任务********************************/
        for (int i = 0; i<3; i++) {
            NSLog(@"A------%@", [NSThread currentThread]);
        }
        for (int i = 0; i<3; i++) {
            NSLog(@"B------%@", [NSThread currentThread]);
        }
    });
    /********************************同步方式执行********************************/
    dispatch_sync(queue, ^{
        /********************************第二次向queue添加任务********************************/
        for (int i = 0; i<3; i++) {
            NSLog(@"C------%@", [NSThread currentThread]);
        }
        for (int i = 0; i<3; i++) {
            NSLog(@"D------%@", [NSThread currentThread]);
        }
    });
    
    dispatch_release(queue);
    
    NSLog(@"E--------%@", [NSThread currentThread]);
}

 输出结果

2016-03-29 22:04:22.972 test1[1122:74978] A------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.972 test1[1122:74978] A------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] A------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] B------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] B------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] B------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] C------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.973 test1[1122:74978] C------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] C------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] D------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] D------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] D------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}
2016-03-29 22:04:22.974 test1[1122:74978] E--------<NSThread: 0x7f9f3a507f20>{number = 1, name = main}

 输出的结果:当执行任务的方式采用的是同步执行的时候,不管是串行队列,还是并发队列,都不会开启子线程<也就是说,并发队列只在异步函数执行任务中会有效(创建子线程), E输出在最后输出,整体发现,任务是在主线程执行,并且任务是按着循序执行的,也就是说同步函数会阻塞线程!

全局队列

全局并发队列 

  GCD默认已经提供了全局的并发队列,供整个应用使用,可以无需手动创建 

/**
     *  <#Description#>
     *
     *  @param identifier#> 队列的优先级 一般也传0
     *  @param flags#>     此参数暂时无用,用0即可
     * 全局并发队列的优先级
         #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
         #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
         #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
         #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

 

全局主队列

  主队列是GCD自带的一种特殊的串行队列
  使用dispatch_get_main_queue()获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();

线程之间通信

从子线程回到主线程
dispatch_async(
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行耗时的异步操作...
      dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主线程,执行UI刷新操作
        });
});

 

在开发中GCD的其他常用API接口

延时执行

 

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    [self performSelector:@selector(task) withObject:nil afterDelay:2.0];
}

- (void)task
{
    NSLog(@"延迟2秒执行---%@", [NSThread currentThread]);
}

 

输出结果

2016-03-29 22:29:43.308 test1[1153:84643] 延迟2秒执行---<NSThread: 0x7fa682e005e0>{number = 1, name = main}

 结果分析:延迟两秒执行task方法,在task所在的线程是主线程

 

一次性代码

 

使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 只执行1次的代码(这里面默认是线程安全的)
});

   在整个程序中,只会执行一次,开发中可用于单例

单例模式

 

单例模式的作用
  可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问
  从而方便地控制了实例个数,并节约系统资源
  
单例模式的使用场合
  
  在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)

 

ARC中,单例模式的实现
在.m中保留一个全局的static的实例
static id _instance;

重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    return _instance;
}
提供1个类方法让外界访问唯一的实例
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    return _instance;
}

实现copyWithZone:方法
- (id)copyWithZone:(struct _NSZone *)zone
{
    return _instance;
}

 

快速迭代

 

// 使用dispatch_apply函数能进行快速迭代遍历
    NSArray *array = @[@"coder_hong", @"zhangsan", @"lishi"];
    NSUInteger count = array.count;
    dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index){
        // 执行10次代码,index顺序不确定
        NSLog(@"%@", array[index]);
    });

输出结果:

2016-03-29 22:45:47.886 test1[1228:91621] coder_hong
2016-03-29 22:45:47.886 test1[1228:91661] lishi
2016-03-29 22:45:47.886 test1[1228:91657] zhangsan

 

 

转载于:https://www.cnblogs.com/mshong/p/5334890.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值