iOS中的多线程

多线程的一些相关概念

什么是进程
  • 在系统中正在运行的一个应用程序。
  • 每个进程之间是独立的,每个进程均运行在其专用而且受保护的内存空间内。

什么是线程

  • 一个进程要想执行任务,必须得有一个线程,而且每一个进程中至少有一个线程
  • 进程的所有任务都在线程中执行

什么是线程的串行

  • 一个线程中的任务都是串行执行的
  • 如果要在一个线程中执行多个任务,那么只能一个一个按顺序来执行这些任务
  • 同一时间内,一个线程只能执行一个任务

比如在一个线程中下载几个文件(文件A,文件B,文件C)
这里写图片描述

多线程

什么是多线程
  • 一个进程中可以开启多条线程,每条线程可以并行执行不同的任务

比如同时开启三条线程分别下载几个文件(文件A,文件B,文件C)
这里写图片描述

多线程原理

1、同一时间,CPU只能处理一条线程,只有一条线程在工作
2、多线程并发,其实是CPU快速的在多条线程之间切换
3、如果多线程切换速度特别快,就造成了多线程并发执行的假象
**注:**如果线程非常多,CPU会在N多线程之间切换,CPU消耗就会特别大,每条线程被调用额频率就会被降低,所有要适当使用多线程

多线程优缺点
优点

1、能适当的提高程序执行效率
2、能够适当的提高资源利用率(CPU,内存利用率)

缺点

1、创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB),栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize:设置,但是必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的创建时间
2、若开启大量线程,会降低线程上的性能,CPU消耗越大
3、多线程使用太多,会使程序设计更加复杂,比如线程间的通信,数据共享等等

多线程在iOS开发中的应用

什么是主线程
  • 一个iOS程序运行后,默认会开启的一条线程,称为:主线程或UI线程
主线程作用
  • 显示\刷新界面
  • 处理UI事件,比如点击事件、滚动事件、拖拽事件等等
主线程使用注意
  • 不要把耗时的操作放在主线程中
  • 耗时的操作会卡住主线程,严重影响流畅度

耗时操作的执行如果把耗时操作放在主线程里,当用户在第5秒时点击按钮时,因为线程里的操作必须是串行的,此时的这个点击事件会排在10秒耗时操作之后,直到10秒的耗时操作结束后,才能执行按钮点击事件,这样就会造成UI卡住的现象,如下图:
这里写图片描述

如果把耗时操作放在子线程里,此刻,主线程和子线程同事进行,当用户在第5秒时点击按钮时,子线程做耗时操作,主线程响应界面操作,所以这样就不会造成UI卡住的现象,所有要把耗时操作放在子线程后台线程中,如图:
这里写图片描述

iOS中多线程的实现方案

这里写图片描述

关于Pthread

- (IBAction)buttonClick:(id)sender {
    //PThread的创建
    pthread_t thread;
    pthread_create(&thread, NULL, run, NULL);
    //PThread的创建
    pthread_t thread1;
    pthread_create(&thread1, NULL, run, NULL);
}
void * run(void *param){
    NSLog(@"当前线程--%@",[NSThread currentThread]);
    for (NSInteger i = 0; i<5000; i++) {
        NSLog(@"-buttonClick-%ld-%@",(long)i,[NSThread currentThread]);
    }
    return NULL;
}

从代码中可以看出Pthread的创建执行其实也是比较简单的,不过实现过程是通过C语言进行的,从创建方法pthread_create(<#pthread_t _Nullable restrict _Nonnull#>, <#const pthread_attr_t restrict _Nullable#>, <#void _Nullable ( _Nonnull)(void * _Nullable)#>, <#void *restrict _Nullable#>)可以看出,第一个参数是需要一个Pthread 对象指针,第三个是需要一个C语言函数方法(就当于OC中绑定的执行方法),至于第二个和第四个参数,暂时没有什么用,可以直接传入NULL
这里写图片描述
数字1811表示的是当前程序所处的进程 ID
数字27155和27156则表示当前所处的子线程 ID
number也可以作为线程的标识
上述代码没有给线程起名字,因此为null
所以我们就可以通过线程ID进行判断是否成功开启了一个子线程

关于NSThread

  • 通过alloc init进行创建
//创建线程
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"jack"];
thread.name = @"my-thread";
thread.threadPriority = 0.1;
//启动线程
[thread start];
  • 通过 detachNewThreadSelector 方式创建并执行线程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"rose"];
  • 隐式创建后自动启动线程
[self performSelectorInBackground:@selector(run:) withObject:@"wahaha"];
采用第一种方式:设置一些线程属性;例如线程 名字,从控制台信息可以看出来,当设置了不同的NSThread对象的优先级属性,可以控制其执行的顺序,优先级越高,越先执行;而设置名字属性后,可以通过调试监控当前所处线程,便于问题分析
第二、三种,创建和操作简单
  • 线程的状态

    当我们新建一个线程对象的时候,系统就会为其分配一块内存,当你调用线程的开始方法时,就相当于把这个线程放在了线程池里面,等待CPU去调用它,当线程池中有多个线程,那么CPU就会在这几个线程之间来回切换,但是当线程调用了sleep或同步锁时,该调用的线程就会被阻塞,当sleep或锁结束时,CPU再次进行切换中,当线程任务执行完,该线程就会释放。

@property (class, readonly, strong) NSThread *currentThread// 获取当前线程
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程
- (void)start; // 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
+ (void)sleepUntilDate:(NSDate *)date;// 进入阻塞状态
+ (void)sleepForTimeInterval:(NSTimeInterval)time;// 进入阻塞状态
+ (void)exit;//强制停止线程,一旦线程停止(死亡)了,就不能再次开启任务

多线程的竞争(线程锁)

一块资源可能被多个线程共享,也就是多个线程可能会访问同一个资源、同一个对象、同一个变量,就会出现线程安全问题
这里写图片描述
当线程A去访问文件时,需要将文件锁住,读取并运算结束后,将其解锁,线程B再去访问,线程B访问时需要再次将文件加锁并运算,结束后再解锁。这样就避免了俩个线程同时访问文件而造成文件错误。

互斥锁使用
  • 格式:@synchronized(锁对象) { // 需要锁定的代码 }
  • **互斥锁的使用前提:**多条线程抢夺同一块资源
  • **注意:**锁定1份代码只用1把锁,用多把锁是无效的
  • 互斥锁的优缺点
    • 优点:能有效防止因多线程抢夺资源造成的数据安全问题
    • 缺点:需要消耗大量的CPU资源
  • 线程同步
    • 线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
    • 互斥锁,就是使用了线程同步技术,保证线程的串行
  • 线程异步
    • 多线程默认的就是异步,这个不需要多做解释
  • 原子和非原子属性
    • atomic:原子属性,默认为setter方法加锁(默认就是atomic),线程安全,需要消耗大量的资源
    • nonatomic:非原子属性,不会为setter方法加锁,适合内存小的移动设备
    • 注:iOS开发中,所有属性都声明为nonatomic,尽量避免多线程抢夺同一资源。尽量将加锁,资源抢夺业务交给服务器

线程间通信

  • 线程间通信
    在一个进程中,线程往往不是孤立存在,多个线程需要经常进行通信,一般表现为一个线程传递数据给另外一个线程,或者在一个线程中执行完一个特定任务后,转到另一个线程继续执行任务
  • 线程间通信常用方法
    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
    
     - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
    
    
  • 示例
    这里写图片描述
    处理UI在主线程,下载图片载子线程,当图片下载完后,回归主线程并显示。
- (IBAction)showImageView:(UIButton *)sender {
    NSLog(@"%@",[NSThread currentThread]);
    [self performSelectorInBackground:@selector(download) withObject:nil];
}

-(void)download{
    NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image =[UIImage imageWithData:data];
    NSLog(@"%@",[NSThread currentThread]);
    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
}

-(void)showImage:(UIImage *)image{
    self.imageView.image = image;
    NSLog(@"%@",[NSThread currentThread]);
}

关于GCD

  • 什么是GCD
    全称Grand Central Dispatch,纯C语言,提供了非常大的函数
  • GCD优势
    CGD是苹果公司为多核的并行运算提供的方案(iOS4开始)
    GCD会自动利用更多的CPU内核
    GCD会自动管理线程生命周期(创建线程、调度线程、销毁线程)程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理的代码
  • GCD俩个核心概念
    任务:执行什么操作
    队列:用来存放任务
  • GCD使用
    定制任务(想做的操作)
    将任务添加到队列当中(GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则,先进先出,后进后出。)
  • GCD执行
    //同步方式执行
    dispatch_sync(dispatch_queue_t  _Nonnull queue, <^(void)block>)
    //异步的方式执行
    dispatch_async(dispatch_queue_t  _Nonnull queue, <^(void)block>)
    
    queue:队列
    block:任务
    
    
  • 同步和异步的区别:
    • 同步:只能在当前线程中执行任务,不具备开启新线程的能力
    • 异步:可以在新的线程中执行任务,具备开启新线程的能力
  • GCD的队列类型
    • 并发队列:可以让多个任务并发执行,并发功能只有在异步dispatch_async函数下才有效
    • 串行队列:让任务一个接着一个地执行

注:有四个容易混淆的概念:同步、异步、并发、串行
同步和异步主要影响能不能开线程
串行和并行主要影响任务的执行方式

GCD并发队列
  • 异步函数 + 并发队列:会开新线程
	//1、创建一个并发队列
	//第一种:自定义(队列的名字,队列的类型)
	//队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)
    //    dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
    
//第二种:获得全局的并发队列(参数1:优先级(官方建议用default),参数2:目前没有意义,官方文档提示传0)
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //2、将任务加入队列
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"1--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"2--%@",[NSThread currentThread]);
        }
    });
   

这里写图片描述

  • 同步函数 + 并发队列 :不会开新线程
	//1、创建一个并发队列
	
	//第一种:自定义(队列的名字,队列的类型)
	//队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)
	//    dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
    
	//第二种:获得全局的并发队列(参数1:优先级(官方建议用default),参数2:目前没有意义,官方文档提示传0)
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //2、将任务加入队列
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"1--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"2--%@",[NSThread currentThread]);
        }
    });

这里写图片描述

GCD串行队列
  • 异步函数 + 串行队列 :会开新线程,但是任务是串行的,执行完一个再执行下一个
//1、创建一个串行队列(没有全局,只能创建)

	//第一种:队列的名字,队列的类型
    //队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_SERIAL);
    
    //第二种:获得全局的并发队列(参数1:队列的名字,参数2:队列的类型(NULL)
//    dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", NULL);

    //2、将任务加入队列
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"1--%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"2--%@",[NSThread currentThread]);
        }
    });

这里写图片描述

  • 同步函数 + 串行队列 :不会开新线程,在当前线程执行任务
   //1、创建一个串行队列(没有全局,只能创建)

	//第一种:队列的名字,队列的类型
    //队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)
    dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_SERIAL);
    
    //第二种:获得全局的并发队列(参数1:队列的名字,参数2:队列的类型(NULL)
//    dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", NULL);]

    //2、将任务加入队列
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"1--%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"2--%@",[NSThread currentThread]);
        }
    });

这里写图片描述

GCD主队列(特殊的串行队列)
  • 放在主队列中的任务,都会放在主线程中执行

  • 使用dispatch_get_main_queue()获得主队列

  • 异步函数 + 主队列 :只在主线程中执行任务

   //获取主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    //2、将任务加入队列
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"1--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<5; i++) {
            NSLog(@"2--%@",[NSThread currentThread]);
        }
    });

这里写图片描述

  • 同步函数 + 主队列 :会卡死,线程间相互制约
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//2、将任务加入队列
dispatch_sync(queue, ^{
    for (NSInteger i = 0; i<5; i++) {
        NSLog(@"1--%@",[NSThread currentThread]);
    }
});
    
dispatch_sync(queue, ^{
    for (NSInteger i = 0; i<5; i++) {
       NSLog(@"2--%@",[NSThread currentThread]);
    }
});

这里写图片描述

  • 各种队列的执行效果
    这里写图片描述
    注:
    1、使用同步函数往当前串行队列中添加任务,会卡住当前的串行队列
    2、使用异步函数是需要把当前方法执行,再去执行异步函数

  • GCD线程间通信

//图片子线程下载
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image =[UIImage imageWithData:data];
        
        //图片主线程显示
        dispatch_sync(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
  • barrier函数
    在barrier函数前面执行的任务执行结束后它后面的任务才会执行
    这个queue不能是全局的队列,最好自己创建,例如:
dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    for (NSInteger i = 0; i<3; i++) {
        NSLog(@"1----%@",[NSThread currentThread]);
    }
});
dispatch_async(queue, ^{
    for (NSInteger i = 0; i<3; i++) {
        NSLog(@"2----%@",[NSThread currentThread]);
    }
});
    
dispatch_barrier_sync(queue, ^{
    NSLog(@"--barrier--%@",[NSThread currentThread]);
});
    
dispatch_async(queue, ^{
    for (NSInteger i = 0; i<3; i++) {
        NSLog(@"3----%@",[NSThread currentThread]);
    }
});
dispatch_async(queue, ^{
    for (NSInteger i = 0; i<3; i++) {
       NSLog(@"4----%@",[NSThread currentThread]);
    }
});

这里写图片描述

  • 延时函数
	//第一种延时
    [self performSelector:@selector(run) withObject:nil afterDelay:2];
    //第二种延时
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"run");
    });
    //第三种延时
    [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:NO];
    NSLog(@"start");
  • 一次性代码
	static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"--程序在整个加载过程只会加载一次");
    });
  • 快速迭代(主要用于并发队列)
//参数1: 指定重复次数
//参数2:对象的DispatchQueue
//参数3:带有参数的Block, index的作用是为了按执行的顺序区分各个Block
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%@--%zu",[NSThread currentThread],index);
    });
NSLog(@"done");

这里写图片描述

GCD队列组
  • 队列组就是可以对多个队列进行操作的一个组,在队列组中可以对不同队列进行操作监听结果等等
	 //创建一个队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //创建一个队列组
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"download image1 start");
        //下载图片1
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        self.image1 =[UIImage imageWithData:data];
        NSLog(@"download image1 end");
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"download image2 start");
        //下载图片2
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        self.image2 =[UIImage imageWithData:data];
        NSLog(@"download image2 end");
    });
    
    //当这个队列组的所有队列全部完成,就会收到这个消息
    dispatch_group_notify(group, queue, ^{
        NSLog(@"download all images");
        //合成新图片
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        //在主线程显示
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });

关于NSOperation

  • 配合使用NSOperation和NSOperationQueue也能实现多线程编程
    • 先将需要执行的操作封装到一个NSOperation对象中
    • 然后将NSOperation对象添加到NSOperationQueue中
    • 系统会自动将NSOperationQueue中的NSOperation取出来
    • 将取出的NSOperation封装的操作放到一条新线程中执行
  • NSOperation的子类(抽象类,并不具备封装操作的能力,必须使用它的子类)
    • NSInvocationOperation
    • NSBlockOperation
    • 自定义子类继承NSOperation,实现内部相应的方法
  • 关于NSInvocationOperation
    调用start方法开始执行操作,一旦执行操作就会调用run方法
    注:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
    只有NSOperation放到一个NSOperationQueue中,才会异步执行
- (IBAction)invocationOperation:(id)sender {
    //初始化Operation子类
    NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
    //开启
    [operation start];
}
-(void)run{
    NSLog(@"0--%@",[NSThread currentThread]);
}

这里写图片描述

  • 关于NSBlockOperation
    注:只要NSBlockOperation封装的操作数大于1,就会异步执行
    //初始化Operation子类
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1--%@",[NSThread currentThread]);
    }];
    //添加额外的任务(在子线程执行)
    [operation addExecutionBlock:^{
        NSLog(@"2--%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        NSLog(@"3--%@",[NSThread currentThread]);
    }];
    [operation start];

这里写图片描述

  • 关于NSOperationQueue
    • NSOperationQueue的作用
      • NSOperation可以调用start方法来执行任务,默认是同步的
      • 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
//创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//NSOperationQueue *queue = [NSOperationQueue mainQueue];

    //创建操作(任务)
    //创建--NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];
    //创建--NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"-1--%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"-2--%@",[NSThread currentThread]);
    }];
    
    //自定义(需要继承NSOperation,执行的操作需要放在这个自定义类的main中)
    SSOperation *op5 = [[SSOperation alloc]init];
    
    //添加任务队列中
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
    //也可以直接创建任务到队列中去
    [queue addOperationWithBlock:^{
        NSLog(@"-3--%@",[NSThread currentThread]);
    }];

通过输出的log可以看出确实有开启线程,所有只要将操作添加的队列里,就可以实现多线程操作
这里写图片描述

  • NSOperationQueue设置最大并发操作数
    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    //设置最大并发操作数(不管加入队列有多少操作,实际队列并发数为3)
    queue.maxConcurrentOperationCount = 3;
    
//    //设置为1就成了串行队列
//    queue.maxConcurrentOperationCount = 1;
    
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"-1--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"-2--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"-3--%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        [NSThread sleepForTimeInterval:2]; // 模拟耗时操作
        NSLog(@"-4--%@",[NSThread currentThread]);
    }];

这里写图片描述

  • NSOperationQueue设置队列挂起与取消
    • 当队列调用了队列挂起的方法( self.queue.suspended = YES;),队列里的执行方法立即停止,但是有一点需要注意的是,当block操作中,队列挂起是不起作用的,它是无法停止的,必须操作执行结束后才会生效。
    • 当队列调用取消( [self.queue cancelAllOperations])就意味着后续队列不再执行,再次启动需要重新加入队列
#pragma mark-OperationQueue相关设置--设置队列挂起(暂停)
- (IBAction)createOperationQueueSuspended:(id)sender {

    //创建队列
    self.queue = [[NSOperationQueue alloc]init];
    self.queue.maxConcurrentOperationCount = 1;
    [self.queue addOperationWithBlock:^{
        NSLog(@"-1--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [self.queue addOperationWithBlock:^{
        NSLog(@"-2--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [self.queue addOperationWithBlock:^{
        NSLog(@"-3--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    [self.queue addOperationWithBlock:^{
        NSLog(@"-4--%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:1.0];
    }];
    //设置队列挂起或者取消的话都必须是在block方法执行完之后才有效
    [self.queue addOperationWithBlock:^{
        for(NSInteger i = 0;i<5;i++){
            NSLog(@"-5--%zd---%@",(long)i,[NSThread currentThread]);
        }
    }];
}
#pragma mark-设置队列挂起
- (IBAction)operationSetSuspended:(id)sender {
    if(self.queue.suspended){
        //恢复队列,继续执行
        self.queue.suspended  = NO;
    }else{
        //挂起(暂停队列)
        self.queue.suspended  = YES;
    }
}
#pragma mark-设置队列取消(取消就意味着后续队列不再执行,再次启动需要重新加入队列)
- (IBAction)operationSetCancel:(id)sender {
    [self.queue cancelAllOperations];
}
  • NSOperationQueue设置队列监听与依赖
 NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down1---%@",[NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"down2---%@",[NSThread currentThread]);
        
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{

        NSLog(@"down3---%@",[NSThread currentThread]);
        
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
       
        NSLog(@"down4---%@",[NSThread currentThread]);
    }];
    
    
    //设置依赖(op1和op3执行完之后才执行2)
    [op3 addDependency:op1];
    [op3 addDependency:op4];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];

    
    //监听一个操作的执行完成
    [op3 setCompletionBlock:^{
        NSLog(@"执行完成");
    }];

这里写图片描述

注:一定要避免相互依赖,比如

[op3 addDependency:op1];
[op1 addDependency:op3];    //错误的写法---相互依赖
  • NSOperationQueue队列间的数据通信
    先创建了一个普通队列,在普通队列里执行俩个操作,当子线程的图片都下载下来后,回归主线程将其显示在UI界面上。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    __block UIImage *image1;
    NSBlockOperation *downloadw1 = [NSBlockOperation blockOperationWithBlock:^{
        
        //下载图片1
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        image1 =[UIImage imageWithData:data];
    }];
    
    __block UIImage *image2;
    NSBlockOperation *downloadw2 = [NSBlockOperation blockOperationWithBlock:^{
        
        //下载图片2
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        image2 =[UIImage imageWithData:data];
        
    }];
    
    
    NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
        
        //合成新图片
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        [image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        [image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    
    [combine addDependency:downloadw1];
    [combine addDependency:downloadw2];
    
    [queue addOperation:downloadw1];
    [queue addOperation:downloadw2];
    [queue addOperation:combine]; 

最后,附上以上的Log版demo,Git:github.com/hejiasu/Multithreading

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值