准备

iOS多线程编程

2018-08-01  本文已影响96人  陵无山

iOS多线程编程

基本知识

1. 进程(process)

2. 线程(thread)

3. 进程和线程的关系

相同点

进程和线程都是有操作系统所提供的程序运行的基本单元,系统利用该基本单元实现系统对应用程序的并发性

不同点

优缺点

4.

多线程

CPU指令列

这里所说的“1个CPU执行的CPU命令列为一条无分叉路径”,即为“线程”

5. 多线程的优缺点

优点

缺点

6. 多线程实际应用

7. 主线程

8. 串行(Serial)和并行(Parallelism)

串行和并行描述的是任务和任务之间的执行方式. 串行是任务A执行完了任务B才能执行, 它们俩只能顺序执行. 并行则是任务A和任务B可以同时执行.

9. 同步(Synchronous)和异步(Asynchronous)

10. 并发(Concurrency)和并行(Parallelism)

Ray大神的示意图和说明来解释一下:

并发是程序的属性(property of the program), 而并行是计算机的属性(property of the machine).


再来解释一下, 并行和并发都是用来让不同的任务可以"同时执行", 只是并发是伪同时, 而并行是真同时. 假设你有任务T1和任务T2(这里的任务可以是进程也可以是线程):

  1. 首先如果你的CPU是单核的, 为了实现"同时"执行T1和T2, 那只能分时执行, CPU执行一会儿T1后马上再去执行T2, 切换的速度非常快(这里的切换也是需要消耗资源的, context switch), 以至于你以为T1和T2是同时执行了(但其实同一时刻只有一个任务占有着CPU).
  2. 如果你是多核CPU, 那么恭喜你, 你可以真正同时执行T1和T2了, 在同一时刻CPU的核心core1执行着T1, 然后core2执行着T2, great!

其实我们平常说的并发编程包括狭义上的"并行"和"并发", 你不能保证你的代码会被并行执行, 但你可以以并发的方式设计你的代码. 系统会判断在某一个时刻是否有可用的core(多核CPU核心), 如果有就并行(parallelism)执行, 否则就用context switch来分时并发(concurrency)执行.

Parallelism requires Concurrency, but Concurrency does not guarantee Parallelism!(并行要求并发性,但并发并不能保证并行性)

iOS中的多线程

类型 简介 实现语言 线程生命周期 使用频率
pthread 1.一套通用的多线程API 2.使用与Unix、Linux/Windows等系统 3.夸平台、可移植 4.使用难度大 C 程序员管理 几乎不用
NSThread 1.使用更加面向对象 2.简单易用,可直接操作线程对象 OC 程序员管理 偶尔使用
GCD 1.旨在替代NSThread等线程技术 2.充分利用设备的多核 3.基于C的底层的API C 自动管理 经常使用
NSOperation 1.是基于GCD实现的Objective-C API 2.比GCD多了一些更简单实用的功能 3.使用更加面向对象 OC 自动管理 经常使用

iOS编程技术一-NSThread

创建线程的方式

  1. 通过NSThread的实例方法

      - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument
    
  2. 通过NSThread的类方法

     + (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
     + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObjects:(nullable id)argument;        
    
  3. 通过NSObject的分类方法

     - (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg
    

NSThread代码Demo

- (IBAction)downloadAction:(UIButton *)sender {
    [self objectNSthreadMethod];
}

//通过NSObject的分类方法开辟线程
- (void)categoryNSthreadMethod {
    [self performSelectorInBackground:@selector(downloadImage) withObject:nil];
}

//通过NSThread类方法开辟线程
- (void)classNSthreadMethod {
    //异步1
    [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];
    //异步2
    [NSThread detachNewThreadWithBlock:^{
        [self downloadImage];
    }];
}

//通过NSThread实例方法去下载图片
- (void)objectNSthreadMethod {
    //创建一个程序去下载图片
//    NSThread *thread = [[NSThread alloc] initWithBlock:^{
//        [self downloadImage];
//    }];
    //or
    NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(downloadImage) object:nil];
    //开启线程
    [thread start];
    thread.name = @"imageThread";
}

//下载图片
- (void)downloadImage {
    NSURL * url = [NSURL URLWithString:@"https://ss3.baidu.com/-rVXeDTa2gU2pMbgoY3K/it/u=2573230712,1194340385&fm=202&mola=new&crop=v1"];
    //线程延迟4s
    [NSThread sleepForTimeInterval:4.0];
    NSData * data = [NSData dataWithContentsOfURL:url];
    
    NSLog(@"downloadImage:%@",[NSThread currentThread].description);//在子线程中下载图片
    
    //在主线程中更新UI
    [self performSelectorOnMainThread:@selector(updateImage:) withObject:data waitUntilDone:YES];
    
}

//更新ImageView
- (void)updateImage:(NSData *)data {
    NSLog(@"updateImage:%@",[NSThread currentThread].description);//在主线程中更新UI
    //将二进制数据转换为Image
    UIImage *image = [UIImage imageWithData:data];
    //设置image
    self.imageView.image = image;
}

线程状态

1.新建状态
2.就绪状态
3.运行状态
4.阻塞状态
5.死亡状态

线程通信

线程在运行过程中,可能需要与其它线程进行通信,如在主线程中修改界面等等,可以使用如下接口:

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)org waitUntilDone:(BOOL)wait

线程的属性

线程的同步与锁

线程的同步与锁什么时候会发生呢?就在我们的线程共用资源的时候,导致资源竞争,举个例子:多个窗口同时售票系统

#import "SellTicketsViewController.h"

@interface SellTicketsViewController () {
    NSInteger tickets;//总票数
    NSInteger count;//当前卖出去的票数
}

@property (nonatomic, strong) NSThread* ticketsThreadOne;
@property (nonatomic, strong) NSThread* ticketsThreadTwo;
@property (nonatomic, strong) NSLock* ticketsLock;
@end

@implementation SellTicketsViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    tickets = 100;
    count = 0;
    
    //锁对象
    self.ticketsLock = [[NSLock alloc] init];
    
    self.ticketsThreadOne = [[NSThread alloc] initWithTarget:self selector:@selector(sellAction) object:nil];
    self.ticketsThreadOne.name = @"thread-1";
    
    self.ticketsThreadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(sellAction) object:nil];
    self.ticketsThreadTwo.name = @"thread-2";
}
- (IBAction)startSell:(id)sender {
    [self.ticketsThreadOne start];
    [self.ticketsThreadTwo start];

}

- (void)sellAction {
    while (true) {
        //上锁
        [self.ticketsLock lock];
        if (tickets > 0) {
            [NSThread sleepForTimeInterval:0.5];
            count = 100 - tickets;
            NSLog(@"当前总票数是:%ld----->卖出:%ld------>线程名:%@",tickets,count,[NSThread currentThread]);
            tickets --;
        }else {
            break;
        }
        //解锁
        [self.ticketsLock unlock];
    }
}
@end

通过上面的Demo应该理解线程的同步以及锁的使用问题

[myLock lock]

资源处理....

[myLock unLock];                     

iOS编程技术二-GCD

GCD基本介绍

任务和队列

任务

就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行异步执行。两者的主要区别是:是否具备开启新线程的能力

队列

这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有四种队列:串行队列、并发队列、主队列、全局队列

  1. 串行队列(Serial Dispatch Queue):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

    • 一次只能"调度"一个任务
    • dispatch_queue_create("queue", NULL);或者dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
  2. 并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)

    • 一次可以"调度"多个任务
    • 并发功能只有在异步(dispatch_async)函数下才有效
    • dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
  3. 主队列

    • 专门用来在主线程上调度任务的队列
    • 不会开启线程
    • 在主线程空闲时才会调度队列中的任务在主线程执行
    • dispatch_get_main_queue();
  4. 全局队列

    • 执行过程和并发队列一致,参考并发队列
    • dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

小结: 在以后的使用中,记住下面的就可以了!

  1. 开不开线程由执行任务的函数决定
    • 异步开,异步是多线程的代名词
    • 同步不开
  2. 开几条线程由队列决定
    • 串行队列开一条线程(GCD会开一条,NSOperation Queue最大并发数为1时也可能开多条)
    • 并发队列开多条线程,具体能开的线程数量由底层线程池决定

GCD的使用

先来看一段代码:异步下载图片

//通过GCD异步下载图片
- (void)downloadImageByDispatchAsync{
    self.imageView.image = nil;
    //获取全局队列
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //异步下载图片
        NSURL *url = [NSURL URLWithString:@"https://ss3.baidu.com/-rVXeDTa2gU2pMbgoY3K/it/u=2573230712,1194340385&fm=202&mola=new&crop=v1"];
        //获取资源转换为二进制
        NSData * data = [NSData dataWithContentsOfURL:url];
        //将二进制转为图片
        UIImage *image = [UIImage imageWithData:data];
        
        //获取主队列,更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            //给图控件赋值
            self.imageView.image = image;
        });
    });
}

NSThread对比可以发现:

1. 串行队列(Serial Dispatch Queue)

串行队列的创建:

dispatch_queue_t queue = dispatch_queue_create("queue",DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create("queue",NULL);

串行队列同步和异步执行Demo:

//串行队列同步
- (void)serialQueueSyncMethod {
    //创建队列
    dispatch_queue_t queue = dispatch_queue_create("serialQueueSyncMethod", DISPATCH_QUEUE_SERIAL);
    //执行任务
    for (int i = 0; i < 6; i++) {
        NSLog(@"mainThread--->%d",i);
        dispatch_sync(queue, ^{
            NSLog(@"Current Thread=%@--->%d----",[NSThread currentThread],i)
        });
    }
    NSLog(@"串行队列同步结束");
}

//串行队列异步执行
- (void)serialQueueAsyncMethod {
    dispatch_queue_t queue = dispatch_queue_create("serialQueueAsyncMethod",DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i < 6; i++) {
        NSLog(@"mainThread--->%d",i);
        dispatch_async(queue,^{
            NSLog(@"Current Thread=%@--->%d----",[NSThread currentThread],i);
        });
    }
    NSLog(@"串行队列异步结束");
}

串行队列,同步执行结果:

2016-11-03 17:16:35.794 ThreadDemo[27088:5268309] mainThread--->0
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->0-----
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->1
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->1-----
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->2
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->2-----
2016-11-03 17:16:35.795 ThreadDemo[27088:5268309] mainThread--->3
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->3-----
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] mainThread--->4
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->4-----
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] mainThread--->5
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] Current Thread=<NSThread: 0x60800007ae40>{number = 1, name = main}---->5-----
2016-11-03 17:16:35.796 ThreadDemo[27088:5268309] 串行队列同步end

串行队列 异步执行结果:

2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->0
2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->1
2016-11-03 17:22:25.074 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->0-----
2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->2
2016-11-03 17:22:25.074 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->1-----
2016-11-03 17:22:25.074 ThreadDemo[27122:5273206] mainThread--->3
2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->2-----
2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] mainThread--->4
2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->3-----
2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] mainThread--->5
2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->4-----
2016-11-03 17:22:25.075 ThreadDemo[27122:5273206] 串行队列异步end
2016-11-03 17:22:25.075 ThreadDemo[27122:5273252] Current Thread=<NSThread: 0x60800007f380>{number = 5, name = (null)}---->5-----

小结:

2. 并发队列(Concurrent Dispatch Queue)

并发队列创建

dispatch_queue_t queue = dispatch_queue_create("queue",DISPATCH_QUEUE_CONCURRENT);

并发队列的同步和异步执行demo

//并发队列同步
- (void)concurrentQueueSyncMethod {
    dispatch_queue_t queue = dispatch_queue_create("concurrentQueueSyncMethod", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 6; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"Current Thread=%@---->%d----", [NSThread currentThread],i);
        });
    }
    NSLog(@"并发队列同步结束");
}

//并发队列异步执行
- (void)concurrentQueueAsyncMethod {
    dispatch_queue_t queue = dispatch_queue_create("concurrentQueueAsyncMethod",DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 6; i++) {
        dispatch_async(queue,^{
            NSLog(@"Current Thread=%@---->%d----",[NSThread currentThread],i);
        });
    }
    NSLog(@"并发队列异步结束");
}  

并发队列同步执行结果:

2016-11-03 17:49:33.850 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->0-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->1-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->2-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->3-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->4-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] Current Thread=<NSThread: 0x60000007bf00>{number = 1, name = main}---->5-----
2016-11-03 17:49:33.851 ThreadDemo[27176:5290096] 并行队列同步end

并发队列异步执行结果:

016-11-03 18:33:32.794 ThreadDemo[27283:5311953] 并行队列异步end
2016-11-03 18:33:32.794 ThreadDemo[27283:5312009] Current Thread=<NSThread: 0x60800026b8c0>{number = 3, name = (null)}---->0-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312006] Current Thread=<NSThread: 0x600000269180>{number = 4, name = (null)}---->1-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312003] Current Thread=<NSThread: 0x600000268f80>{number = 6, name = (null)}---->3-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312174] Current Thread=<NSThread: 0x60800026b740>{number = 8, name = (null)}---->5-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312004] Current Thread=<NSThread: 0x60800026b7c0>{number = 5, name = (null)}---->2-----
2016-11-03 18:33:32.794 ThreadDemo[27283:5312173] Current Thread=<NSThread: 0x60800026b800>{number = 7, name = (null)}---->4-----

小结:

3. 全局队列(Global Dispatch Queue)

全局队列基本知识

全局队列同步和异步执行Demo

//全局队列同步
- (void)globalSyncMethod {
    //获取全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    //执行任务
    for (int i = 0; i < 10; i++ ) {
        dispatch_sync(queue, ^{
            NSLog(@"global_queue_sync%@---->%d----",[NSThread currentThread],i);
        });
    }
    NSLog(@"global_queue_sync_end");
}

//全局队列异步执行
- (void)globalAsyncMethod {
    //获取全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    //执行任务
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"global_queue_async%@---->%d----",[NSThread currentThread],i);
        });
    }
    NSLog(@"globalAsyncMethod end");
}

全局队列 同步执行结果:

2016-11-03 19:06:02.650 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->0----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->1----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->2----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->3----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->4----
2016-11-03 19:06:02.651 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->5----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->6----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->7----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->8----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync<NSThread: 0x600000072200>{number = 1, name = main}---->9----
2016-11-03 19:06:02.652 ThreadDemo[27347:5324064] global_queue_sync_end

全局队列 异步执行结果:

2016-11-03 19:12:00.242 ThreadDemo[27430:5333537] global_queue_async_end
2016-11-03 19:12:00.242 ThreadDemo[27430:5334057] global_queue_async<NSThread: 0x608000266100>{number = 5, name = (null)}---->0----
2016-11-03 19:12:00.242 ThreadDemo[27430:5334053] global_queue_async<NSThread: 0x600000263c80>{number = 6, name = (null)}---->1----
2016-11-03 19:12:00.242 ThreadDemo[27430:5334056] global_queue_async<NSThread: 0x600000263cc0>{number = 7, name = (null)}---->2----
2016-11-03 19:12:00.242 ThreadDemo[27430:5334063] global_queue_async<NSThread: 0x608000268500>{number = 8, name = (null)}---->3----
2016-11-03 19:12:00.242 ThreadDemo[27430:5334064] global_queue_async<NSThread: 0x600000263b80>{number = 9, name = (null)}---->4----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334057] global_queue_async<NSThread: 0x608000266100>{number = 5, name = (null)}---->5----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334053] global_queue_async<NSThread: 0x600000263c80>{number = 6, name = (null)}---->7----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334065] global_queue_async<NSThread: 0x600000073600>{number = 10, name = (null)}---->6----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334056] global_queue_async<NSThread: 0x600000263cc0>{number = 7, name = (null)}---->8----
2016-11-03 19:12:00.243 ThreadDemo[27430:5334066] global_queue_async<NSThread: 0x608000267d40>{number = 11, name = (null)}---->9----
4.主队列 (Main Dispatch Queue)

主队列基本知识

主队列同步和异步执行demo

//主队列同步
- (void)mainSyncMethod {
    //获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    //执行任务
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"main_queue_sync%@---->%d----",[NSThread currentThread],i);
        });
    }
    NSLog(@"main_queue_sync_end");
}

//主队列异步
- (void)mainAsyncMethod {
    //创建
    dispatch_queue_t queue = dispatch_get_main_queue();
    //执行
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"main_queue_async%@--->%ld---",[NSThread currentThread],i);
        });
    }
    NSLog(@"main_queue_async_end");
}

主队列 同步执行结果:

主线程和主队列相互等待造成死锁,程序会直接卡死!
原因:源代码在Main Dispatch Queue 即主队列中执行指定的block任务,并等待其结束。
而其实在主线程中正在执行这些源代码,所以无法执行追加到Main Dispatch Queue 的block任务。
下面例子也一样:
dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        dispatch_sync(queue, ^{
            NSLog(@"main_queue_sync%@----",[NSThread currentThread]);
        });
    });

主队列 异步执行结果:

2016-11-03 19:45:38.154 ThreadDemo[27501:5349956] main_queue_async_end
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->0----
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->1----
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->2----
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->3----
2016-11-03 19:45:38.155 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->4----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->5----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->6----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->7----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->8----
2016-11-03 19:45:38.156 ThreadDemo[27501:5349956] main_queue_async<NSThread: 0x608000070880>{number = 1, name = main}---->9----
5.延迟执行 (dispatch_after)

延迟执行(dispatch_after)Demo

- (void)GCDAfterRunMethod {
    //循环5次
    for (int i = 0; i < 10000;i++) {
        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2*NSEC_PER_SEC));
        dispatch_after(time, dispatch_get_main_queue(), ^ {
            NSLog(@"This is my %d number!%@",i,[NSThread currentThread]);
        });
    }
}

注意:

6. 更改优先级(dispatch_set_target_queue)※

dispatch_set_target_queue第一个Demo:

- (void)GCDSetTargetQueueMethod{
    
     dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    
     dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
     dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
      //更改优先级
     dispatch_set_target_queue(queue1, targetQueue);
    
     for (int i = 0; i < 6; i++) {
         dispatch_async(queue1, ^{
         NSLog(@"queue1-currentThread = %@-->%d",[NSThread currentThread],i);
         });
     }
     for (int i = 0; i < 6; i++) {
          dispatch_async(queue2, ^{
          NSLog(@"queue2-----currentThread = %@----->%d",[NSThread currentThread],i);
          });
     }
 }

打印结果:

2016-11-04 11:14:57.034 ThreadDemo[28477:5530919] queue2-----currentThread = <NSThread: 0x60000007e0c0>{number = 11, name = (null)}----->1
2016-11-04 11:14:57.034 ThreadDemo[28477:5530920] queue2-----currentThread = <NSThread: 0x60000007a140>{number = 12, name = (null)}----->2
2016-11-04 11:14:57.034 ThreadDemo[28477:5530918] queue2-----currentThread = <NSThread: 0x60000007a100>{number = 10, name = (null)}----->0
2016-11-04 11:14:57.034 ThreadDemo[28477:5530919] queue2-----currentThread = <NSThread: 0x60000007e0c0>{number = 11, name = (null)}----->3
2016-11-04 11:14:57.034 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->0
2016-11-04 11:14:57.034 ThreadDemo[28477:5530920] queue2-----currentThread = <NSThread: 0x60000007a140>{number = 12, name = (null)}----->5
2016-11-04 11:14:57.034 ThreadDemo[28477:5530921] queue2-----currentThread = <NSThread: 0x60000007c340>{number = 13, name = (null)}----->4
2016-11-04 11:14:57.035 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->1
2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->2
2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->3
2016-11-04 11:14:57.036 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->4
2016-11-04 11:14:57.037 ThreadDemo[28477:5530912] queue1-currentThread = <NSThread: 0x60000007ec00>{number = 9, name = (null)}-->5

打印解释:

在上面全局队列中说到了优先级的事情
因为XNU内核用于Global Dispatch Queue的线程并不能保证实时性,因此执行优先级知识大概的判断和区分,所有我们不能完全依赖这个优先级来做队列的顺序事情,否则会出现问题!切记

dispatch_set_target_queue第二个Demo:

- (void)GCDSetTargetQueueMethod{
     dispatch_queue_t targetQueue = dispatch_queue_create("targetQueue", DISPATCH_QUEUE_SERIAL);//目标队列
     dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);//串行队列
     dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);//并发队列
     //设置参考
     dispatch_set_target_queue(queue1, targetQueue);
     dispatch_set_target_queue(queue2, targetQueue);
    
     for (int i = 0; i < 6; i++) {
       dispatch_async(queue1, ^{
           NSLog(@"queue1-currentThread = %@-->%d",[NSThread currentThread],i);
       });
   }
   for (int i = 0; i < 6; i++) {
         dispatch_async(queue2, ^{
             NSLog(@"queue2-currentThread = %@-->%d",[NSThread currentThread],i);
         });
     }
    
 }

打印结果:

2016-11-04 11:34:34.722 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->0
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->1
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->2
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->3
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->4
2016-11-04 11:34:34.723 ThreadDemo[28551:5540844] queue1-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->5
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->0
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->1
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->2
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->3
2016-11-04 11:34:34.724 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->4
2016-11-04 11:34:34.725 ThreadDemo[28551:5540844] queue2-currentThread = <NSThread: 0x60000007b640>{number = 3, name = (null)}-->5

打印解释:

dispatch_set_target_queue小结:

7. 任务组Dispatch Group※

GCD的任务组在开发中是经常被使用到,==当你一组任务结束后再执行一些操作时,使用任务组在合适不过了==。dispatch_group的职责就是当队列中的所有任务都执行完毕后在去做一些操作,也就是说在任务组中执行的队列,当队列中的所有任务都执行完毕后就会发出一个通知来告诉用户任务组中所执行的队列中的任务执行完毕了。关于将队列放到任务组中执行有两种方式:

1.队列与组自动关联并执行
首先我们来介绍dispatch_group_async()函数的使用方式,该函数会将队列与相应的任务组进行关联,并且自动执行。当与任务组关联的队列中的任务都执行完毕后,会通过dispatch_group_notify()函数发出通知告诉用户任务组中的所有任务都执行完毕了。==使用通知的方式是不会阻塞当前线程的==,如果你==使用dispatch_group_wait()函数,那么就会阻塞当前线程==,直到任务组中的所有任务都执行完毕。

//自动执行任务组
- (void)GCDAutoDispatchGroupMethod {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    for (int i = 0; i<6; i++) {
        dispatch_group_async(group,queue, ^{
            NSLog(@"current Thread = %@----->%d",[NSThread currentThread],i);
        });
    }
    
    dispatch_group_notify(group,dispatch_get_main_queue(), ^{
        NSLog(@"current Thread = %@----->这是最后执行",[NSThread currentThread]);
    });
}

打印结果:

2016-11-04 15:11:34.010 ThreadDemo[29225:5641180] current Thread = <NSThread: 0x600000468200>{number = 5, name = (null)}----->2
2016-11-04 15:11:34.010 ThreadDemo[29225:5641193] current Thread = <NSThread: 0x60800007f900>{number = 3, name = (null)}----->0
2016-11-04 15:11:34.011 ThreadDemo[29225:5641438] current Thread = <NSThread: 0x608000467980>{number = 8, name = (null)}----->5
2016-11-04 15:11:34.010 ThreadDemo[29225:5641178] current Thread = <NSThread: 0x608000467640>{number = 6, name = (null)}----->3
2016-11-04 15:11:34.010 ThreadDemo[29225:5641177] current Thread = <NSThread: 0x6080004675c0>{number = 4, name = (null)}----->1
2016-11-04 15:11:34.010 ThreadDemo[29225:5641437] current Thread = <NSThread: 0x600000467f40>{number = 7, name = (null)}----->4
2016-11-04 15:11:34.011 ThreadDemo[29225:5641137] current Thread = <NSThread: 0x60800007e200>{number = 1, name = main}----->这是最后执行

2.队列与组手动关联并执行

//手动执行任务组
 - (void)GCDManualDispatchGroupMethod{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    
    for (int i = 0; i < 6; i++) {
        
        dispatch_group_enter(group);//进入队列组
        
        dispatch_async(queue, ^{
            NSLog(@"current Thread = %@----->%d",[NSThread currentThread],i);
            
            dispatch_group_leave(group);//离开队列组
        });
    }
    
    long result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//阻塞当前线程,直到所有任务执行完毕才会继续往下执行
    if (result == 0) {
        //属于Dispatch Group 的block任务全部处理结束
        NSLog(@"Dispatch Group全部处理完毕");

    }else{
        //属于Dispatch Group 的block任务还在处理中
        NSLog(@"Dispatch Group正在处理");
    }
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"current Thread = %@----->这是最后执行",[NSThread currentThread]);
    });
 }

打印结果: dispatch_group_wait()函数下方的print()函数在所有任务执行完毕之前是不会被调用的,因为dispatch_group_wait()会将当前线程进行阻塞。当然虽然是手动的将队列与任务组进行关联的,感觉display_group_notify()函数还是好用的。

2016-11-04 16:19:13.802 ThreadDemo[29402:5678525] current Thread = <NSThread: 0x600000271780>{number = 5, name = (null)}----->2
2016-11-04 16:19:13.802 ThreadDemo[29402:5678527] current Thread = <NSThread: 0x608000077300>{number = 3, name = (null)}----->0
2016-11-04 16:19:13.802 ThreadDemo[29402:5678524] current Thread = <NSThread: 0x60800007d840>{number = 4, name = (null)}----->1
2016-11-04 16:19:13.803 ThreadDemo[29402:5678760] current Thread = <NSThread: 0x600000271cc0>{number = 8, name = (null)}----->5
2016-11-04 16:19:13.802 ThreadDemo[29402:5678545] current Thread = <NSThread: 0x60800007d000>{number = 6, name = (null)}----->3
2016-11-04 16:19:13.802 ThreadDemo[29402:5678544] current Thread = <NSThread: 0x60800007d040>{number = 7, name = (null)}----->4
2016-11-04 16:19:13.803 ThreadDemo[29402:5678489] Dispatch Group全部处理完毕
2016-11-04 16:19:13.804 ThreadDemo[29402:5678489] current Thread = <NSThread: 0x600000069dc0>{number = 1, name = main}----->这是最后执行
9.栅栏任务Dispatch_barrier_async
#pragma mark - Dispatch_barrier_async
 - (void)GCDBarrierAsyncMethod{
    
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    void(^blk1_reading)(void) = ^{
         NSLog(@"blk1---reading");
    };
    
    void(^blk2_reading)(void) = ^{
        NSLog(@"blk2---reading");
    };
    void(^blk3_reading)(void) = ^{
        NSLog(@"blk3---reading");
    };
    void(^blk4_reading)(void) = ^{
        NSLog(@"blk4---reading");
    };
    void(^blk_writing)(void) = ^{
        NSLog(@"blk---writing");
    };
        
    dispatch_async(concurrentQueue, blk1_reading);
    dispatch_async(concurrentQueue, blk2_reading);
    
    //添加追加操作,,会等待b1和b2全部执行结束,执行完成追加操作b,才会继续并发执行下面操作
    dispatch_barrier_async(concurrentQueue, blk_writing);
    
    dispatch_async(concurrentQueue, blk3_reading);
    dispatch_async(concurrentQueue, blk4_reading);

}

打印结果:

2016-11-04 17:02:13.202 ThreadDemo[29492:5700974] blk2---reading
2016-11-04 17:02:13.202 ThreadDemo[29492:5700972] blk1---reading
2016-11-04 17:02:13.203 ThreadDemo[29492:5700972] blk---writing
2016-11-04 17:02:13.203 ThreadDemo[29492:5700972] blk3---reading
2016-11-04 17:02:13.203 ThreadDemo[29492:5700974] blk4---reading

使用Concurrent Dispatch Queue和dispatch_barrier_async函数可实现高效率的数据库访问和文件访问,dispatch_barrier_async函数还是比较好理解的。

9. 循环执行dispatch_apply
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index){
     NSLog(@"%zu", index);
});
    
NSLog(@"done");
2016-11-04 17:24:25.819 ThreadDemo[29598:5715955] 2
2016-11-04 17:24:25.819 ThreadDemo[29598:5715904] 0
2016-11-04 17:24:25.819 ThreadDemo[29598:5716588] 1
2016-11-04 17:24:25.819 ThreadDemo[29598:5716640] 3
2016-11-04 17:24:25.820 ThreadDemo[29598:5715955] 4
2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] 5
2016-11-04 17:24:25.820 ThreadDemo[29598:5716588] 6
2016-11-04 17:24:25.820 ThreadDemo[29598:5716640] 7
2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] 9
2016-11-04 17:24:25.820 ThreadDemo[29598:5715955] 8
2016-11-04 17:24:25.820 ThreadDemo[29598:5715904] done

#pragma mark - Dispatch_apply
 - (void)GCDDispatchApplyMethod{
   
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    void(^blk1_reading)(void) = ^{
        NSLog(@"blk1---reading");
    };
    
    void(^blk2_reading)(void) = ^{
        NSLog(@"blk2---reading");
    };
    void(^blk3_reading)(void) = ^{
        NSLog(@"blk3---reading");
    };
    void(^blk_writing)(void) = ^{
        NSLog(@"blk---writing");
    };

    NSMutableArray *array = [NSMutableArray new];
    [array addObject:blk1_reading];
    [array addObject:blk2_reading];
    [array addObject:blk3_reading];
    [array addObject:blk_writing];
    
    dispatch_async(queue, ^{
        dispatch_apply(array.count, queue, ^(size_t index) {
            void (^blk)(void) = [array objectAtIndex:index];
            blk();
            NSLog(@"%zu====%@",index,[array objectAtIndex:index]);
        });
        NSLog(@"全部执行结束");
        dispatch_async(dispatch_get_main_queue(), ^{
            //在main Dispatch queue中执行处理,更新用户界面等待
            NSLog(@"done");
        });
    });

}

打印结果:

2016-11-04 17:36:02.258 ThreadDemo[29635:5723967] blk3---reading
2016-11-04 17:36:02.258 ThreadDemo[29635:5723940] blk2---reading
2016-11-04 17:36:02.258 ThreadDemo[29635:5723929] blk1---reading
2016-11-04 17:36:02.258 ThreadDemo[29635:5723968] blk---writing
2016-11-04 17:36:02.259 ThreadDemo[29635:5723940] 1====<__NSGlobalBlock__: 0x102895af0>
2016-11-04 17:36:02.259 ThreadDemo[29635:5723967] 2====<__NSGlobalBlock__: 0x102895b30>
2016-11-04 17:36:02.259 ThreadDemo[29635:5723929] 0====<__NSGlobalBlock__: 0x102895ab0>
2016-11-04 17:36:02.259 ThreadDemo[29635:5723968] 3====<__NSGlobalBlock__: 0x102895b70>
2016-11-04 17:36:02.259 ThreadDemo[29635:5723940] 全部执行结束
2016-11-04 17:36:02.259 ThreadDemo[29635:5723499] done
10. 队列的挂起和唤醒
#pragma mark -Dispatch_suspend/Dispatch_resume
- (void)GCDDispatch_suspend_resume{
    //系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。
    
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", 0);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", 0);
    
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_async(queue1, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"%@-------%d",[NSThread currentThread],i);
            sleep(1);         
        }
    });
    
    dispatch_async(queue2, ^{
        NSLog(@"task2");
    });
    
    dispatch_group_async(group, queue1, ^{
        NSLog(@"task1 finished!");
    });
    dispatch_group_async(group, queue2, ^{
       dispatch_suspend(queue1);//挂起
       NSLog(@"task2 finished!挂起queue1");
       [NSThread sleepForTimeInterval:20.0];
       dispatch_resume(queue1);//唤醒队列
        
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    dispatch_async(queue1, ^{
        NSLog(@"task3");
    });
    dispatch_async(queue2, ^{
        NSLog(@"task4");
    });
}

打印结果:可以先思考一下打印结果

2016-11-04 17:59:41.219 ThreadDemo[29749:5738141] task2
2016-11-04 17:59:41.219 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------0
2016-11-04 17:59:41.220 ThreadDemo[29749:5738141] task2 finished!挂起queue1
2016-11-04 17:59:42.220 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------1
2016-11-04 17:59:43.223 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------2
2016-11-04 17:59:44.225 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------3
2016-11-04 17:59:45.230 ThreadDemo[29749:5738138] <NSThread: 0x60000027c380>{number = 4, name = (null)}-------4
2016-11-04 18:00:01.220 ThreadDemo[29749:5738141] task1 finished!
2016-11-04 18:00:01.220 ThreadDemo[29749:5738141] task3
2016-11-04 18:00:01.221 ThreadDemo[29749:5738769] task4
11. 信号量Dispatch Semaphore
- (void)GCDDispatchSemaphore{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
  //创建一个信号量dispatch
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
    NSMutableArray *array = [NSMutableArray new];
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            //一直等待信号量大于0
            dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER));
            
          //进行排他控制的处理代码...
          //将dispatch semphore的计数器减1 
            [array addObject:[NSNumber numberWithInteger:i]];
              
            //排他控制处理结束
            //将dispatch semphore的计数器加1    
            //如果有通过dispatch_semaphore_wait函数dispatch semphore的计数值增加的线程,等待就由最先等待的线程执行                  
            dispatch_semaphore_signal(semaphore);
        });
        
        NSLog(@"%@",array);
    }
}
创建一个信号量dispatch dispatch_semaphore_t dispatch_semaphore_create(long value);
等待信号量longdispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);返回值和dispatch_group_wait函数相同,也可以通过下面进行分支处理:
long  result = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10*NSEC_PER_SEC)));
            
 if (result == 0){
      //进行排他控制的处理代码...
      //将dispatch semphore的计数器减1 
      [array addObject:[NSNumber numberWithInteger:i]];
              
      //排他控制处理结束
      //将dispatch semphore的计数器加1    
      //如果有通过dispatch_semaphore_wait函数dispatch semphore的计数值增加的线程,等待就由最先等待的线程执行                  
      dispatch_semaphore_signal(semaphore);
}else{
      //这个时候计数值为0,在达到指定时间为止还是等待状态。
        //处理逻辑....
}

在进行排他处理结束时,通过dispatch_semaphore_signal来讲计数值加1

GCD总结

GCD的使用步骤
  1. 创建block任务
  2. 创建GCD队列(串行和并发)
  3. 把block任务放到GCD队列里,以异步或者同步方式执行GCD队列

iOS编程技术三-NSOperation

一. NSOperation基本介绍

1.NSOperation与GCD

NSOperation是苹果提供给我们的一套多线程解决方案。实际上NSOperation是基于GCD更高一层的封装,但是比GCD更简单易用、代码可读性也更高,下面是NSOperation和GCD的优缺点:

2.NSOperation的使用步骤

因为NSOperation是基于GCD的,那么使用起来也和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列。NSOperation实现多线程的使用步骤分为三步:

  1. 创建任务:先将需要执行的操作封装到一个NSOperation对象中
  2. 创建队列:创建NSOperationQueue对象
  3. 将任务加入到队列中:然后将NSOperation对象添加到NSOperationQueue中

之后,系统就会自动将NSOperationQueue中的NSOperation取出来,在新线程中执行操作。

二. NSOperation和NSOperationQueue的基本使用

创建任务

1. 使用子类NSInvocationOperation
NSInvocationOperation:我们可以通过一个 object 和 selector 非常方便地创建一个NSInvocationOperation,这是一种非常动态和灵活的方式。

创建方法:

- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)arg;

使用demo:

- (void)invocationOperation {
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    op.completionBlock = ^ {
        NSLog(@"任务完成后回调block");
    };
    [op start];
}

- (void)run {
    NSLog(@"----%@",[NSThread currentThread]);
}

结果:

2018-07-13 11:28:20.4 MultipleThreadDemo[2405:1751612] ----<NSThread: 0x1c4070600>{number = 1, name = main}
2018-07-13 11:28:20.4 MultipleThreadDemo[2405:1751723] 任务完成后回调block

2. 使用子类NSBlockOperation
NSBlockOperation:我们可以使用 NSBlockOperation 来并发执行一个或多个 block ,只有当一个 NSBlockOperation 所关联的所有 block 都执行完毕时(会阻塞当前线程),这个 NSBlockOperation 才算执行完成,有点类似于 dispatch_group 的概念。

方法:

+ (instancetype)blockOperationWithBlock:(void (^)(void))block;
- (void)addExecutionBlock:(void (^)(void))block;

Demo:

- (void)blockOperationAddTask {
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^ {
        NSLog(@"I'm running---%@",[NSThread currentThread].description);
    }];
    for (int i = 0; i < 20; i++) {
        [op addExecutionBlock:^{
            NSLog(@"added block running---%@",[NSThread currentThread].description);
        }];
    }
    NSLog(@"op start");
    [op start];
    NSLog(@"op end");
}

打印的结果:

op start
I'm running---<NSThread: 0x1c0063fc0>{number = 1, name = main}
added block running---<NSThread: 0x1c0473800>{number = 4, name = (null)}
added block running---<NSThread: 0x1c0063fc0>{number = 1, name = main}
added block running---<NSThread: 0x1c0473800>{number = 4, name = (null)}
added block running---<NSThread: 0x1c0063fc0>{number = 1, name = main}
added block running---<NSThread: 0x1c0473800>{number = 4, name = (null)}
added block running---<NSThread: 0x1c0063fc0>{number = 1, name = main}
added block running---<NSThread: 0x1c0473800>{number = 4, name = (null)}
op end

NSBlockOperation注意点:
从上面打印结果看到在多个线程执行任务。addExecutionBlock:可以为NSBlockOperation添加额外的操作。如果当前NSOperation的任务只有一个的话,那肯定不会开辟一个新的线程,只能同步执行。只有NSOperation的任务数>1的时候,这些额外的操作才有可能在其他线程并发执行。注意我的用词 "才有可能",也就是说额外的操作也有可能在当前线程里执行。
3. 使用自定义的NSOperation的子类
NSOperation的子类:当系统预定义的两个子类 NSInvocationOperation 和 NSBlockOperation 不能很好的满足我们的需求时,我们可以自定义自己的 NSOperation 子类,添加我们想要的功能。我们可以自定义非并发并发两种不同类型的 NSOperation 子类,而自定义一个前者要比后者简单得多

非并发的NSOperation子类Demo:

先定义一个继承自NSOperation的子类,重写main方法
SerialOperation.h

#import <Foundation/Foundation.h>

typedef void(^CompletionBlock)(NSDate* imageData);
@interface SerialOperation : NSOperation
@property (nonatomic, copy) CompletionBlock comBlock;
@end

SerialOperation.m

#import "SerialOperation.h"

@implementation SerialOperation
- (void)main {
    @autoreleasepool {
        if (self.isCancelled) {
            return;
        }
        NSURL *url = [NSURL URLWithString:@""];
        NSData * imageData = [NSData dataWithContentsOfURL:url];
        
        if (!imageData) {
            imageData = nil;
        }
        
        if (self.isCancelled) return;
        [self performSelectorOnMainThread:@selector(completionAction:) withObject:imageData];
    }
}

@end

调用:

//在主线程中执行并没有开辟线程
- (void)serialOperation {
    SerialOperation * op = [[SerialOperation alloc] init];
    op.comBlock = ^(NSData* imageData) {
        self.imageView.image = [UIImage imageWithData:imageData];
    };
    [op start];
    
}

三. 创建队列

和GCD中的并发队列、串行队列略有不同的是:NSOperationQueue一共有两种队列:主队列非主队列。其中非主队列==同时包含了串行、并发功能==。下边是主队列、其他队列的基本创建方法和特点。

主队列
凡是添加到主队列中的任务(NSOperation),都会放到主线程中执行

NSOperationQueue *queue = [NSOperationQueue mainQueue];

非主队列

四. 把任务加入到队列中

只要将任务加入到队列中,就不要执行start方法,队列会负责调度任务自动执行start方法。加入队列的方法如下:

- (void)addOperation:(NSOperation *)op;//添加单个任务
- (void)addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait;//添加任务数组
- (void)addOperationWitBlock:(void(^)(void))block;//添加单个任务

*1. - (void)addOperation:(NSOperation )op;
首先创建任务operation,然后将创建好的任务添加队列!

//多任务的operation
- (void)addOperationToQueue {
    // 1.创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 2.创建操作
    // 创建NSInvocationOperation
    NSInvocationOperation * op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2) object:nil];
    // 创建NSBlockOperation
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"op2--->%d----%@",i,[NSThread currentThread]);
        }
    }];
    
    // 3.添加操作到队列中:addOperation:
    [queue addOperation:op1];//[op1 start];
    [queue addOperation:op2];//[op2 start];
}

- (void)run2 {
    for (int i=0; i < 5; i++) {
        NSLog(@"op1--->%d----%@",i,[NSThread currentThread]);
    }
}

打印结果:

op1--->0----<NSThread: 0x1c047f240>{number = 9, name = (null)}
op1--->1----<NSThread: 0x1c047f240>{number = 9, name = (null)}
op2--->0----<NSThread: 0x1c4274540>{number = 10, name = (null)}
op2--->1----<NSThread: 0x1c4274540>{number = 10, name = (null)}
op2--->2----<NSThread: 0x1c4274540>{number = 10, name = (null)}
op2--->3----<NSThread: 0x1c4274540>{number = 10, name = (null)}
op2--->4----<NSThread: 0x1c4274540>{number = 10, name = (null)}
op1--->2----<NSThread: 0x1c047f240>{number = 9, name = (null)}
op1--->3----<NSThread: 0x1c047f240>{number = 9, name = (null)}
op1--->4----<NSThread: 0x1c047f240>{number = 9, name = (null)}

从上面可以看到NSOperation Queue会开辟线程。然后并发执行!
2. - (void)addOperationWithBlock:(void (^ )(void))block;
无需先创建任务,在block中添加任务,直接将任务block加入到队列中

- (void)addOperationWithBlockToQueue {
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    // 设置最大并发操作数
    // queue.maxConcurrentOperationCount = 1;//就变成了串行队列
    queue.maxConcurrentOperationCount = 5;
    for (int i = 0; i < 5; i++) {
        [queue addOperationWithBlock:^{
            NSLog(@"%d---%@",i,[NSThread currentThread]);
        }];
    }
}

结果:

2016-11-07 22:13:14.189 ThreadDemo[2933:60785] 2-----<NSThread: 0x600000274840>{number = 10, name = (null)}
2016-11-07 22:13:14.189 ThreadDemo[2933:60814] 0-----<NSThread: 0x608000260f00>{number = 14, name = (null)}
2016-11-07 22:13:14.189 ThreadDemo[2933:60803] 1-----<NSThread: 0x600000275600>{number = 11, name = (null)}
2016-11-07 22:13:14.189 ThreadDemo[2933:60812] 4-----<NSThread: 0x600000274a40>{number = 13, name = (null)}
2016-11-07 22:13:14.189 ThreadDemo[2933:60695] 3-----<NSThread: 0x608000260a00>{number = 9, name = (null)}

可以看出addOperationWithBlock:和NSOperationQueue能够开启新线程,进行并发执行。
3. maxConcurrentOperationCount

代码参考上一个- (void)addOperationWithBlock:(void (^)(void))block ;Demo,修改最大并发数即可测试,结果如下:

//maxConcurrentOperationCount=1
2016-11-08 10:05:34.748 ThreadDemo[1224:14753] 0-----<NSThread: 0x600000263100>{number = 4, name = (null)}
2016-11-08 10:05:34.749 ThreadDemo[1224:14753] 1-----<NSThread: 0x600000263100>{number = 4, name = (null)}
2016-11-08 10:05:34.749 ThreadDemo[1224:14753] 2-----<NSThread: 0x600000263100>{number = 4, name = (null)}
2016-11-08 10:05:34.750 ThreadDemo[1224:14754] 3-----<NSThread: 0x6000002635c0>{number = 3, name = (null)}
2016-11-08 10:05:34.750 ThreadDemo[1224:14754] 4-----<NSThread: 0x6000002635c0>{number = 3, name = (null)}

注意点:

五. 任务的操作依赖

通过配置依赖关系,我们可以让不同的 operation 串行执行,正如我们上面刚刚提到的最大并发数为1时串行执行(但是顺序不一定会是我们想要的顺序),一个 operation 只有在它依赖的所有 operation 都执行完成后才能开始执行。配置 operation 的依赖关系主要涉及到NSOperation 类中的以下两个方法:

- (void)addDependency:(NSOperation *)op;//添加依赖
- (void)removeDependency:(NSOperation *)op;//删除依赖

Demo:

//依赖操作
- (void)operateDependency {
    NSMutableArray * array = [NSMutableArray array];
    
    //创建任务
    for (int i = 0; i < 10; i++) {
        NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"----第%d个任务%@----", i,[NSThread currentThread]);
        }];
        op.name = [NSString stringWithFormat:@"op%d",i];
        [array addObject:op];
    }
    
    //创建队列
    NSOperationQueue * queue = [[NSOperationQueue alloc] init];
    queue.name = @"queue";
    
    //设置依赖 可以跨队列依赖
    for (int i = 0; i < array.count - 1; i++) {
        //依次依赖,下面相当于同步执行了
        NSBlockOperation * opOne = [array objectAtIndex:i];
        NSBlockOperation * opTwo = [array objectAtIndex:i+1];
        [opTwo addDependency:opOne];
        
        //修改Operation在队列中的优先级
//        if (i == 6) {
//            [opOne setQueuePriority:NSOperationQueuePriorityVeryLow];
//            [opTwo setQueuePriority:NSOperationQueuePriorityVeryHigh];
//        }
//
        if (i > 4) {
            //删除依赖
//            [opTwo removeDependency:opOne];
        }
        
    }
    //第五个任务完成后就取消对列任务
    NSBlockOperation *op = [array objectAtIndex:4];
    op.completionBlock = ^{
        //取消队列中未执行的所有任务
        [queue cancelAllOperations];
    };
    
    //添加任务到队列中
    [queue addOperations:array waitUntilFinished:NO];
}

六. NSOperation方法介绍

NSOperation方法

BOOL cancelled;//判断是否取消
BOOL executing;//判断任务是否正在执行
BOOL finished;//判断任务是否完成
BOOL concurrent;//判断任务是否并发
NSOPerationQueuePriority queuePriority;//修改operation执行任务线程的优先级
void (^completionBlock)(void)//用来设置完成后需要执行的操作
- (void)cancel;//取消任务
- (void)waitUntilFinish;//阻塞当前线程直到次任务完成

NSOperationQueue方法

NSUInteger operationCount; //获取队列的任务数
- (void)cancelAllOperations;//取消队列中所有的任务
- (void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的所有任务执行完毕
[queue setSuspended:BOOL];//暂停queue

取消任务

任务在队列中的优先级

completionBlock

暂停和恢复 Operation Queue

七. 并发的NSOperation

在上面创建自定义子类NSOperation任务的时候只是创建了串行的NSOperation子类,只要重写main方法即可。现在我们就来看看如何实现并发的子类NSOperation。

NSOperation有三个状态量isCancelled, isExecutingisFinished.

实现并发(concurrent)的NSOperation步骤:

1. 重写start()函数

必须的,所有并发执行的 operation 都必须要重写这个方法,替换掉 NSOperation 类中的默认实现。start 方法是一个 operation 的起点,我们可以在这里配置任务执行的线程或者一些其它的执行环境。另外,需要特别注意的是,在我们重写的 start 方法中一定不要调用父类的实现

2. 重写main函数

可选的,通常这个方法就是专门用来实现与该 operation 相关联的任务的。尽管我们可以直接在 start 方法中执行我们的任务,但是用 main 方法来实现我们的任务可以使设置代码和任务代码得到分离,从而使 operation 的结构更清晰

3. isExecuting 和 isFinished

4. 重写isConcurrent函数

必须的,这个方法的返回值用来标识一个 operation 是否是并发的 operation ,我们需要重写这个方法并返回 YES

并发的NSOperationDemo

#import <Foundation/Foundation.h>
typedef void (^CompletionBlock)(NSData* imageData);
@interface ConcurrentOperation : NSOperation
@property (nonatomic, copy) CompletionBlock comBlock;
@end
#import "ConcurrentOperation.h"

@interface ConcurrentOperation()<NSURLConnectionDelegate,NSURLConnectionDataDelegate>

@property (nonatomic, strong) NSURLConnection *connection;
@property (nonatomic, strong) NSMutableData *data;
@property (nonatomic, assign) CFRunLoopRef operationRunLoop;

@end


@implementation ConcurrentOperation
@synthesize executing = _executing;
@synthesize finished = _finished;

- (BOOL)isConcurrent {
    return YES;
}

- (void)start {
    if (self.isCancelled) {
        [self finish];
    }
    [self willChangeValueForKey:@"isExecuting"];
    _executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
    
    NSURL *url = [NSURL URLWithString:@"https://ss3.baidu.com/-rVXeDTa2gU2pMbgoY3K/it/u=2573230712,1194340385&fm=202&mola=new&crop=v1"];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    _connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
    
    /*
     if (![NSThread isMainThread])
     {
     [self performSelectorOnMainThread:@selector(start)
     withObject:nil
     waitUntilDone:NO];
     return;
     }
     // set up NSURLConnection...
     or
     
     [[NSOperationQueue mainQueue] addOperationWithBlock:^{
     self.connection = [NSURLConnection connectionWithRequest:self.request delegate:self];
     }];
     */
    
    NSOperationQueue *currentQueue = [NSOperationQueue currentQueue];
    BOOL backgroundQueue = (currentQueue != nil && currentQueue != [NSOperationQueue mainQueue]);
    NSRunLoop *targetRunloop = (backgroundQueue)?[NSRunLoop currentRunLoop]:[NSRunLoop mainRunLoop];
    [self.connection scheduleInRunLoop:targetRunloop forMode:NSRunLoopCommonModes];
    [self.connection start];
    
    if (backgroundQueue) {
        self.operationRunLoop = CFRunLoopGetCurrent();
        CFRunLoopRun();
    }
}

- (void)cancel {
    if (!_executing) {
        return;
    }
    [super cancel];
    [self finish];
}

- (void)finish {
    self.connection = nil;
    [self willChangeValueForKey:@"isExecuting"];
    [self willChangeValueForKey:@"isFinished"];
    _executing = NO;
    _finished = YES;
    
    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
    
    if (self.comBlock) {
        self.comBlock(_data);
    }
}
#pragma mark - NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    // to do something...
    self.data       = [NSMutableData data];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    // to do something...
    NSLog(@"%ld",data.length);
    [_data appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    if (self.operationRunLoop) {
        CFRunLoopStop(self.operationRunLoop);
    }
    if (self.isCancelled) return;
    
    [self finish];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    [self finish];
}
@end

并发使用:

//并发的NSOperation子类使用
- (void)concurrentNSOperation {
    //子队列运行
    ConcurrentOperation * op = [[ConcurrentOperation alloc]init];
    op.comBlock = ^(NSData *imageData) {
        NSLog(@"before set uiimage iamge:%@",[NSThread currentThread]);
        UIImage *image = [UIImage imageWithData:imageData];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
            NSLog(@"after set uiimage iamge:%@",[NSThread currentThread]);
        });
    };
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:op];
}

并发NSOperation的Demo一些解释:
代码中引入了RunLoop的东西->原因呢:

Modifying Running Code

线程安全问题

参考文章https://developer.apple.com/library/archive/technotes/tn2002/tn2059.html#Downloads

上一篇下一篇

猜你喜欢

热点阅读