美文网首页
多线程03 阻塞barrier 延迟 一次性 单例 调度组 NS

多线程03 阻塞barrier 延迟 一次性 单例 调度组 NS

作者: xwf_code | 来源:发表于2016-10-20 20:27 被阅读0次

阻塞barrier 延迟 一次性 单例 调度组 NSOperation 同时并发数 依赖操作

模拟网络延迟

 [NSThread sleepForTimeInterval:1.0];

GCD阻塞

使用GCD的barrier,必须使用自定义的并发队列

自定义的并发队列
dispatch_queue_t queue = dispatch_queue_create("hm", DISPATCH_QUEUE_CONCURRENT);

dispatch_barrier_async(queue, ^{
                
                NSLog(@"barrier %@ - %@",name,[NSThread currentThread]);
                [self.imagesM addObject:image];
            });

当需要多线程环境下,操作线程不安全的类(属性)
如在多个线程中向一个NSArray可变数组添加数据
需要如上代码所示 使用barrier会单开一个线程单独的操作线程非安全的类

GCD延迟执行

延迟默认是异步执行的

dispatch_after(when, queue, block);

如上图所示需要三个参数 以下是完整版

// 参数1 : 延迟的时间 : NSEC_PER_SEC : 10亿纳秒 (非常精确,比NSTimer要精确)
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));
// 参数2 : 延迟任务执行的队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 参数3 : 延迟的任务
dispatch_block_t block = ^ {
NSLog(@"哈哈 %@",[NSThread currentThread]);
};

//添加参数后
dispatch_after(when, queue, block);


GCD一次性执行
代码演示
  • (void)onceDemo02
    {
    for (NSInteger i = 0; i < 1000; i++) {

      NSLog(@"asdfghjkasdfghjasdfghj");
      
      dispatch_async(dispatch_get_global_queue(0, 0), ^{
          
          static dispatch_once_t onceToken;
          dispatch_once(&onceToken, ^{
              NSLog(@"鸟哥");
          });
      });
    

    }
    }

以上代码中"鸟哥"只执行一次
onceToken被保存在静态存储区,**地址唯一**;
有个初始值是0;当第一次执行时,会检测初始值是否为0;
如果是0就执行代码块;
反之,就不执行代码块;
提示,当第一次执行完之后,会修改onceToken的初始值为非0的值;

#once的使用场景 : 单例模式的设计
**单例模式 **
有一个全局访问点
对象有且只有一个
保存在静态存储区
生命周期和APP一样长

**单例的使用场景**
在APP中,必须有且只有一个对象的类,要设计成单例模式 (音乐播放器...)
在APP开发中,有些类会经常频繁的使用,比如一些工具类(网络请求工具类,数据库管理工具类...)

**单例的缺点**
只要创建了单例对象,就会一直占用着一块内存,知道程序退出,单例对象的内存空间才释放
单例能过多的使用,滥用;
单例分两种 : 懒汉式和饿汉式;实际开发使用懒汉式就够了;饿汉式仅作了解
面试时,有可能手写单例
单例是一种设计模式而已,类似于MVC...设计模式;
实际开发中,不需要重写根开辟内存空间和实例化相关的任何方法 如allocwithzone

// 懒汉式单例 : 尽量创建的晚;开发中单例的创建方式

  • (instancetype)sharedTool
    {
    // 定义静态对象
    static id instance;

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    instance =[[self alloc] init];
    });

    return instance;
    }


#CGD调度组

// 组
dispatch_group_t group = dispatch_group_create();

// 队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

// 这个异步函数,是把异步任务添加到队列,并且向(group)里面添加了一个标记
dispatch_group_async(group, queue, ^{
NSLog(@"假装下载图片A %@",[NSThread currentThread]);
});

// 监听group里面的任务是否执行完 : 一般检测到下载结束之后,在这里面刷新UI的操作
// dispatch_group_notify 是在子线程执行的,需要回到主线程刷新UI
// 一旦检测到group里面的标记没有了,就开始自动调用dispatch_group_notify方法
dispatch_group_notify(group, queue, ^{
NSLog(@"图片下载完了吗? %@",[NSThread currentThread]);
写回到主线程刷新UI的操作
});


#NSOperation

1.他是一个抽象类,无法直接使用;
因为只有定义,无实现;
实现交给子类;
他的作用就是作为父类,约束子类"共有"的属性和方法

2.子类
NSInvocationOperation
NSBlockOperation
自定义NSOpration

3.队列NSOperationQueue

 4.GCD的核心概念 : 将任务添加到队列

 5.OP的核心概念 : 将操作添加到队列

** 6.OP的使用步骤**
创建队列 默认并发的

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

创建操作

NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@(i)];

将操作添加到队列

[queue addOperation:op];

调用(在当前线程)

[op start];


线程间通信 : 常见代码
已在.m文件中创建属性NSOperationQueue *queue;
并在- (void)viewDidLoad 中实例化

[self.queue addOperationWithBlock:^{
NSLog(@"假装在下载... %@",[NSThread currentThread]);

    // 如果异步任务执行后,拿到结果.就通知主线程刷新UI
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        NSLog(@"假装在刷新UI... %@",[NSThread currentThread]);
    }];
}];

#操作服务质量(优先级)和监听操作是否结束

// 操作1
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{

    for (NSInteger i = 0; i < 5; i++) {
        NSLog(@"op1 %zd - %@",i,[NSThread currentThread]);
    }
}];

// 设置op1的服务质量最高 : 服务质量等价于优先级,决定了队列里面的某些操作有更多的机会被队列调度执行
op1.qualityOfService = NSQualityOfServiceUserInteractive;

// 监听某个操作是否执行结束 : 一旦操作执行完毕,底层会自动回调completion
op1.completionBlock= ^{
NSLog(@"op1执行完了吗? %@",[NSThread currentThread]);
};

// 把操作添加到队列
[self.queue addOperation:op1];


#队列的最大并发数
即系统同时执行的线程数量

// 实例化队列
self.queue = [[NSOperationQueue alloc] init];
// 设置队列最大并发数
self.queue.maxConcurrentOperationCount = 2;

**暂停任务**
继续则赋值为YES即可

// 当队列里面没有操作时,不需要挂起队列
if (self.queue.operationCount == 0) {
return;
}
// 使队列暂停调度任务
self.queue.suspended = YES;

1.正在执行的操作,无法暂停
2.operationCount : 是记录队列里面的操作个数,但是,只会记录没有被队列调度的和调度了但是没有执行完的操作
3.一旦先挂起队列,再添加操作到队列,这个操作是可以成功的添加进队列;但是,无法被队列调度执行


**取消 / 移除队列里面所有的操作**

[self.queue cancelAllOperations];

1.正在执行的操作无法取消
2.如果你非要取消正在执行的操作,需要自定义NSOperation
3.取消全部操作,会有一定的时间延迟;具体延迟多久,是系统自己算的
4.为什么会有延迟?
队列一旦调用了 cancelAllOperations,队列会遍历自己里面的所有操作
每遍历出一个操作,就会调用cancel方法
一旦操作对象调用了cancel方法,那么操作对象的cancelled属性,就会被设置成YES;表示该操作不能正常执行
一旦队列发现,操作对象的cancelled属性为YES;就不会调度其执行;会移除掉,operationCount不会对其记录


#操作间的依赖

模拟需求 : 登录 --> 付费 --> 下载 --> 通知用户
[付费 addDependency: 登录] ---> 付费依赖于登录
**注意点**
一定要先建立依赖关系,再把操作添加到队列
可以跨队列建立依赖关系
不能建立循环依赖
不能把同一个操作分别添加到两个队列
  • (void)opDemo
    {
    // 登录
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"登录 %@",[NSThread currentThread]);
    }];

    // 付费
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"付费 %@",[NSThread currentThread]);
    }];

    // 下载
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"下载 %@",[NSThread currentThread]);
    }];

    // 通知用户
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"通知用户 %@",[NSThread currentThread]);
    }];

    /*
    1.一定要先建立依赖关系,再把操作添加到队列
    2.可以跨队列建立依赖关系
    3.不能建立循环依赖
    4.不能把同一个操作分别添加到两个队列
    */

    // 添加依赖关系
    [op2 addDependency:op1]; // 付费依赖登录
    [op3 addDependency:op2]; // 下载依赖付费
    [op4 addDependency:op3]; // 通知用户依赖下载

    // 循环依赖
    // [op1 addDependency:op4];

    // 批量把操作添加到队列
    // waitUntilFinished : 不用等待前面的四个异步任务执行完,就可以执行后面的代码
    [self.queue addOperations:@[op1,op2,op3] waitUntilFinished:NO];

    // op4应该被添加到主队列 : 不能把同一个操作分别添加到两个队列
    [[NSOperationQueue mainQueue] addOperation:op4];

    NSLog(@"后面的代码");
    }

相关文章

  • 多线程03 阻塞barrier 延迟 一次性 单例 调度组 NS

    阻塞barrier 延迟 一次性 单例 调度组 NSOperation 同时并发数 依赖操作 模拟网络延迟 GCD...

  • GCD的常用方法

    1、barrier方法2、延迟执行的常用方法3、一次性执行代码4、快速迭代5、队列组 1、barrier方法,注意...

  • Swift多线程:GCD进阶,单例、信号量、任务组

    Swift多线程:GCD进阶,单例、信号量、任务组 Swift多线程:GCD进阶,单例、信号量、任务组

  • iOS多线程之GCD

    主队列: 全局队列: 创建一个并行队列: 调度组: 阻塞执行: 控制代码只执行一次(例如单例): 延时执行: 重复...

  • 设计模式(创建型--单例模式)

    1.饿汉式单例(立即加载方式) 2.懒汉式单例(延迟加载方式) 多线程环境下使用syhchronized 利用双重...

  • 多线程:6单例模式和多线程

    单例模式和多线程 1.立即加载 2.延迟加载 2.1 延迟加载不同步在多线程环境下的问题 2.2延迟加载的解决方案...

  • GCD小总结

    单例模式 串行队列同步/异步执行任务 并发队列同步/异步执行任务 队列组 延时执行 barrier

  • 单例模式

    单例(Singleton)模式 非延迟加载的模式就不介绍了 说一下多线程下的延迟加载 简单加锁 就是在getIns...

  • swift单例

    普通版单例 多线程版单例

  • GCD 让线程同步

    1.dispatch_group 调度组2.dispatch_barrier 栅栏块3.dispatch_sema...

网友评论

      本文标题:多线程03 阻塞barrier 延迟 一次性 单例 调度组 NS

      本文链接:https://www.haomeiwen.com/subject/qmtquttx.html