iOS开发网络多线程之多线程

一. 基本概念

1. 进程

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

    1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。

2. 进程中的线程运行状态

1> 单线程: 串行执行任务

    1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。

2> 多线程: 并行执行任务

    1个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务。


注:多线程并发执行任务的原理

    在同一时间里,CPU只能处理1条线程,只有1条线程在工作(执行)。多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象


3. 多线程的优缺点

  • 优点

    1)能适当提高程序的执行效率。

    2)能适当提高资源利用率(CPU、内存利用率)

  • 缺点

    1)开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。

    2)线程越多,CPU在调度线程上的开销就越大。

    3)程序设计更加复杂:比如线程之间的通信、多线程的数据共享


4. 多线程在iOS开发中应用

  • 4.1 主线程

    1)一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”。

    2)作用。刷新显示UI,处理UI事件。

  • 4.2 使用注意

    1)不要将耗时操作放到主线程中去处理,会卡住线程。

    2)和UI相关的刷新操作必须放到主线程中进行处理。


5. iOS中多线程的实现方法

  • 5.1 pthread

特点:

    1)一套通用的多线程API

    2)适用于Unix\Linux\Windows等系统

    3)跨平台\可移植

    4)使用难度大

使用语言:c语言

使用频率:几乎不用

线程生命周期:由程序员进行管理


  • 5.2 NSThread

特点:

    1)使用更加面向对象

    2)简单易用,可直接操作线程对象

使用语言:OC语言

使用频率:偶尔使用

线程生命周期:由程序员进行管理


  • 5.3 GCD

特点:

    1)旨在替代NSThread等线程技术

    2)充分利用设备的多核(自动)

使用语言:C语言

使用频率:经常使用

线程生命周期:自动管理


  • 5.4 NSOperation

特点:

    1)基于GCD(底层是GCD)

    2)比GCD多了一些更简单实用的功能

    3)使用更加面向对象

使用语言:OC语言

使用频率:经常使用

线程生命周期:自动管理



二. 线程详解


1. PThread创建线程

第一个参数:线程对象地址

第二个参数:线程属性

第三个参数:指向函数的指针

​第四个参数:传递给该函数的参数

  1. // 1.PThread线程创建
  2. - (void)pthread
  3. {
  4.    pthread_t pthread;
  5.    
  6.    // 1.用pthead创建线程
  7.    pthread_create(&pthread, nil, run, nil);
  8. }
  9. void *run(void *param)
  10. {
  11.    NSLog(@"%s", __func__);
  12.    NSLog(@"%@", [NSThread currentThread]);
  13.    return nil;
  14. }


2.NSThread创建线程


1> 通过alloc创建

  1. // NSThread线程的创建 : 方法一
  2. /*
  3. 通过alloc创建可以拿到线程对象,需要手动启动线程
  4. 线程执行完之后,系统自动销毁
  5. */
  6. - (void)thread1
  7. {
  8.    // 方法一 :
  9.    NSThread *thread = [[LDThread alloc] initWithTarget:self selector:@selector(running) object:nil];
  10.    
  11.    thread.name = @"thread";
  12.    
  13.    [thread start];
  14. }

2> 分离出一条子线程

  1. // NSThread线程的创建 : 方法二 : 分离出一条子线程
  2. - (void)thread2
  3. {
  4.    // 方法二 :
  5.    [NSThread detachNewThreadSelector:@selector(running) toTarget:self withObject:nil];
  6. }

3> 创建一条后台子线程

  1. // NSThread线程的创建 : 方法三 : 创建一条后台子线程
  2. - (void)thread3
  3. {
  4.    [self performSelectorInBackground:@selector(running) withObject:nil];
  5. }


4> NSThread线程安全问题

    对于线程访问公有的资源会引起数据混乱

    解决方案:加锁, 当前线程访问时,将访问资源加锁,其他线程在外等待,等待当前线程访问完之后再访问之后再加锁

    锁必须是一把是公有的,建议直接使用当前控制器

    案例:卖票,有3条线程相当于售票窗口,共同卖100张票,直到票卖完为止,如按普通开启3条线程,如当前票是100张,售票窗口A先去查看当前余票是100张,它拿出卖出一张,再把剩余99张票数放到数据库,而当A取出票的同时B也在取看到的余票也是100,卖出一张把自己计算出的剩余票99张放到数据库中把A计算的余票覆盖掉,这样卖出了2张票,而余票还是99张就造成了数据的混乱

    解决方案就是:将访问公共资源时加锁

    代码如下:

  • 创建3条线程

  1. - (void)viewDidLoad {
  2.    [super viewDidLoad];
  3.    
  4.    self.ticketCount = 100;
  5.    
  6.    // 线程1
  7.    NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket) object:nil];
  8.    threadA.name = @"售票员A";
  9.    self.threadA = threadA;
  10.    [threadA start];
  11.    
  12.    // 线程2
  13.    NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket) object:nil];
  14.    threadB.name = @"售票员B";
  15.    self.threadB = threadB;
  16.    [threadB start];
  17.    
  18.    NSThread *threadC = [[NSThread alloc] initWithTarget:self selector:@selector(sellTicket) object:nil];
  19.    threadC.name = @"售票员C";
  20.    self.threadC = threadC;
  21.    [threadC start];
  22.    
  23. }
  • 加锁方案 用 @synchronized(self){}将访问公共的资源包装起来

  1. - (void)sellTicket
  2. {
  3.    while (1) {
  4.        
  5.        // 对于线程访问公有的资源为防止数据混乱
  6.        // 解决方案:加锁, 当前线程访问时,将访问资源加锁,其他线程在外等待,等待当前线程访问完之后再访问之后再加锁
  7.        // 锁必须是一把是公有的,建议直接使用当前控制器
  8.        @synchronized(self) {
  9.            
  10.        NSInteger count = self.ticketCount;
  11.    
  12.        if (count > 0) {
  13.            self.ticketCount = count - 1;
  14.            [NSThread sleepForTimeInterval:0.1];
  15.            NSLog(@"%@卖了一张票, 还剩%zd张票, 线程%@", [NSThread currentThread].name, self.ticketCount, [NSThread currentThread]);
  16.        } else {
  17.            NSLog(@"%@已卖完票", [NSThread currentThread].name);
  18.            break;
  19. //            [NSThread exit];
  20.        }
  21.            
  22.        }
  23.        
  24.    }
  25. }

3. NSThread线程间的通信(下载图片)

1> 点击按钮开启一条线程

  1. - (IBAction)btnClick {
  2.    
  3.    // 开启一条线程
  4.    [NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];
  5.    
  6. }


2> 实现线程方法

    下载图片是耗时任务,放到子线程中执行,刷新图片放在主线程中执行

  1. // 下载图片
  2. - (void)download
  3. {
  4. //    NSLog(@"%@", [NSDate date]);
  5.    // 1.创建URL路径
  6.    NSURL *url = [NSURL URLWithString:@"http://src.house.sina.com.cn/imp/imp/deal/3f/e1/d/2c0401b364ae649b34e7d6999e4_p24_mk24_wm200_s500X0.png"];
  7.    
  8. //    NSLog(@"%@", [NSDate date]);
  9.    
  10.    // 执行开始时间
  11.    CFTimeInterval start = CFAbsoluteTimeGetCurrent();
  12.    
  13.    // 2.加载二进制
  14.    NSData *data = [NSData dataWithContentsOfURL:url];
  15.    
  16.    // 结束执行时间
  17.    CFTimeInterval end = CFAbsoluteTimeGetCurrent();
  18.    
  19.    NSLog(@"消耗 %f s", end - start);
  20.    
  21. //    NSLog(@"%@", [NSDate date]);
  22.    
  23.    // 3.将二进制转为图片
  24.    UIImage *image = [UIImage imageWithData:data];
  25.    
  26.    // 4.刷新imageView(主进程)
  27.    [self performSelector:@selector(reloadImageV:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
  28. }
  29. - (void)reloadImageV:(UIImage *)image
  30. {
  31.    self.imageView.image = image;
  32. }

3> Xcode 7 需要设置允许加载http请求,修改info.plist文件,添加红色标记的键值对


4> 计算代码块执行时间

第一种方法

    NSDate *start = [NSDate date];

    //2.根据url地址下载图片数据到本地(二进制数据)

    NSData *data = [NSData dataWithContentsOfURL:url];


    NSDate *end = [NSDate date];

    NSLog(@"第二步操作花费的时间为%f",[end timeIntervalSinceDate:start]);


第二种方法

    CFTimeInterval start = CFAbsoluteTimeGetCurrent();

    NSData *data = [NSData dataWithContentsOfURL:url];


    CFTimeInterval end = CFAbsoluteTimeGetCurrent();

    NSLog(@"第二步操作花费的时间为%f",end - start);

4.GCD

1> 基本知识

队列和任务

同步函数和异步函数


2> 基本使用

01 异步函数+:开启一条线程,串行执行任务

03 同步函数+并发队并发队列:开启多条线程,并发执行任务

02 异步函数+串行队列列:不开线程,串行执行任务

04 同步函数+串行队列:不开线程,串行执行任务

05 异步函数+主队列:不开线程,在主线程中串行执行任务

06 同步函数+主队列:不开线程,串行执行任务(注意死锁发生)

07 注意同步函数和异步函数在执行顺序上面的差异


3> 具体代码实现

  • 1.GCD 异步函数并行队列(系统提供了一个获取全局并行队列)

  1. - (void)asyncConcurrent
  2. {
  3.    // 异步函数并行队列: 会开启多条线程, 无序执行
  4.    
  5.    // 1.并行队列
  6.    /*
  7.     DISPATCH_QUEUE_CONCURRENT : 并行队列
  8.     DISPATCH_QUEUE_SERIAL : 串行队列
  9.     第一参数: 队列的标识名, 主要用于程序奔溃时,根据奔溃log中的标识名快速定位到队列中的哪个代码块导致
  10.     第二个参数: 队列的属性, 是串行队列还是并行队列
  11.     */
  12. //    dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT);
  13.    
  14.    // 并行全局队列
  15.    /*
  16.     #define DISPATCH_QUEUE_PRIORITY_HIGH 2
  17.     #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
  18.     #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
  19.     #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
  20.     第一个参数: 队列优先级
  21.     */
  22.    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  23.    
  24.    // 2.异步并行队列
  25.    for (int i = 0; i < 50; ++i) {
  26.        
  27.        dispatch_async(queue, ^{
  28.            
  29.            NSLog(@"%d - %@", i, [NSThread currentThread]);
  30.            
  31.        });
  32.    }
  33. }
  • 2.GCD 异步函数串行队列
  1. - (void)asyncSerial
  2. {
  3.    // 异步函数串行队列: 只会创建一条线程, 有序执行
  4.    // 1.串行队列
  5.    dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_SERIAL);
  6.    
  7.    // 2.异步函数
  8.    dispatch_async(queue, ^{
  9.        
  10.        for (int i = 0; i < 10; ++i) {
  11.            NSLog(@"%d - %@", i, [NSThread currentThread]);
  12.        }
  13.        
  14.    });
  15. }
  • 3.GCD 同步并行队列

  1. - (void)syncConcurrent
  2. {
  3.    // 同步并行队列 : 不会创建线程且在当前中执行
  4.    // 1.创建并行队列
  5.    dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT);
  6.    
  7.    // 2.创建同步函数
  8.    dispatch_sync(queue, ^{
  9.        
  10.        for (int i = 0; i < 10; ++i) {
  11.            NSLog(@"%i - %@", i, [NSThread currentThread]);
  12.        }
  13.        
  14.    });
  15. }
  • 4.GCD 同步串行队列

  1. // 4.GCD 同步串行队列
  2. - (void)syncSerial
  3. {
  4.    // 同步并行队列 : 不会创建线程且在当前中执行
  5.    // 1.创建串行队列
  6.    dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.com", DISPATCH_QUEUE_SERIAL);
  7.    
  8.    
  9.    // 2.创建同步函数
  10.    dispatch_sync(queue, ^{
  11.        
  12.        for (int i = 0; i < 10; ++i) {
  13.            NSLog(@"%@", [NSThread currentThread]);
  14.        }
  15.        
  16.    });
  17. }
  • 5.GCD 异步函数主队列

  1. // 5.GCD 异步函数主队列
  2. - (void)asyncMain
  3. {
  4.    // 异步函数主队列 : 串行执行
  5.    // 1.获取主队列
  6.    dispatch_queue_t queue = dispatch_get_main_queue();
  7.    
  8.    // 2.创建异步函数
  9.    dispatch_async(queue, ^{
  10.        
  11.        for (int i = 0; i < 10; ++i) {
  12.            NSLog(@"%i - %@", i, [NSThread currentThread]);
  13.        }
  14.        
  15.    });
  16. }
  • 6.GCD 同步函数主队列

  1. // 6.GCD 同步函数主队列
  2. - (void)syncMain
  3. {
  4.    // 同步函数主队列 : 会照成死锁现象
  5.    // 原因 : 同步函数是串行执行,且必须执行,队列是主函数,而要执行的任务也在主函数,这样就会造成死锁现象
  6.    // 1.获取主队列
  7.    dispatch_queue_t queue = dispatch_get_main_queue();
  8.    
  9.    NSLog(@"----");
  10.    
  11.    // 2.创建同步函数
  12.    dispatch_sync(queue, ^{
  13.        for (int i = 0; i < 10; ++i) {
  14.            NSLog(@"%i - %@", i, [NSThread currentThread]);
  15.        }
  16.    });
  17. }


4> GCD线程间的通信

  1. // 点击按钮
  2. - (IBAction)btnClick {
  3.    
  4.    
  5.    // 创建异步函数并发队列
  6.    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  7.        
  8.        // 获取url路径
  9.        NSURL *url = [NSURL URLWithString:@"http://www.chinanews.com/cr/2014/0108/1576296051.jpg"];
  10.        
  11.        // 加载二进制
  12.        NSData *data = [NSData dataWithContentsOfURL:url];
  13.        
  14.        // data转成image
  15.        UIImage *image = [UIImage imageWithData:data];
  16.        
  17.        // 异步函数主函数 刷新UI
  18.        dispatch_async(dispatch_get_main_queue(), ^{
  19.            self.imageView.image = image;
  20.        });
  21.        
  22.    });
  23.    
  24. }


5> GCD其他函数的使用

  • 1.延迟执行

  1. // 1.延迟执行
  2. - (void)delay
  3. {
  4.    NSLog(@"------start-------");
  5.    
  6.    // 方法1 : NSObject 方法
  7. //    [self performSelector:@selector(run) withObject:self afterDelay:2.0];
  8.    
  9.    // 方法2 : 定时器
  10. //    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
  11.    
  12.    // 方法3 : GDC延迟执行
  13.    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  14.        [self run];
  15.    });
  16. }
  • 2.栅栏函数 : 栅栏函数不能用全局并行队列,否则栅栏函数不起作用

  1. // 2.栅栏函数 : 栅栏函数不能用全局并行队列,否则栅栏函数不起作用
  2. - (void)barrier
  3. {
  4.    dispatch_async(dispatch_get_global_queue(0, 0), ^{
  5.        for (int i = 0; i < 10; ++i) {
  6.            NSLog(@"01----%@", [NSThread currentThread]);
  7.        }
  8.    });
  9.    
  10.    
  11.    dispatch_async(dispatch_get_global_queue(0, 0), ^{
  12.        for (int i = 0; i < 10; ++i) {
  13.            NSLog(@"02----%@", [NSThread currentThread]);
  14.        }
  15.    });
  16.    
  17.    
  18.    // 栅栏函数,保证上面的函数执行完毕后再执行栅栏函数后面的函数(不能用全局并行队列)
  19.    dispatch_barrier_async(dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT), ^{
  20.        NSLog(@"**********************************");
  21.    });
  22.    
  23.    dispatch_async(dispatch_get_global_queue(0, 0), ^{
  24.        for (int i = 0; i < 10; ++i) {
  25.            NSLog(@"03----%@", [NSThread currentThread]);
  26.        }
  27.    });
  28.    
  29.    
  30.    dispatch_async(dispatch_get_global_queue(0, 0), ^{
  31.        for (int i = 0; i < 10; ++i) {
  32.            NSLog(@"04----%@", [NSThread currentThread]);
  33.        }
  34.    });
  35. }
  • 3.一次性函数

  1. // 3.一次性函数
  2. - (void)once
  3. {
  4.    NSLog(@"xxxx");
  5.    
  6.    static dispatch_once_t onceToken;
  7.    dispatch_once(&onceToken, ^{
  8.        // 此代码块只会执行一次
  9.        NSLog(@"once");
  10.    });
  11. }
  • 4.迭代函数

  1. // 4.迭代函数
  2. - (void)iterative
  3. {
  4.    // for循环迭代 : 有序执行
  5.    for (int i = 0; i < 10; ++i) {
  6.        NSLog(@"for = %i - %@", i, [NSThread currentThread]);
  7.    }
  8.    
  9.    // GCD 迭代 : 无序执行
  10.    /*
  11.     第一参数: 迭代次数
  12.     第二个参数: 队列类型
  13.     第三个参数: 索引
  14.     */
  15.    dispatch_apply(10, dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT), ^(size_t index) {
  16.        NSLog(@"apply = %zd - %@", index, [NSThread currentThread]);
  17.    });
  18. }
  • 5.迭代的应用(剪切文件)

  1. // 5.迭代的应用(剪切文件)
  2. - (void)iterativeTest
  3. {
  4.    // 1.创建文件管理器
  5.    NSFileManager *mgr = [NSFileManager defaultManager];
  6.    
  7.    // 2.源和目标文件夹路径
  8.    NSString *fromPath = @"/Users/admin/Desktop/from";
  9.    NSString *toPath = @"/Users/admin/Desktop/to";
  10.    
  11.    // 3.获取源文件夹下的所有文件
  12.    NSArray *fileArray = [mgr subpathsAtPath:fromPath];
  13.    NSLog(@"%@", fileArray);
  14.    
  15.    // 4.for循环拼接文件路径剪切文件
  16.    for (NSString *fileName in fileArray) {
  17.        // 拼接源文件路径
  18.        NSString *fromFile = [fromPath stringByAppendingPathComponent:fileName];
  19.        // 拼接目的文件路径
  20.        NSString *toFile = [toPath stringByAppendingPathComponent:fileName];
  21.        
  22.        [mgr moveItemAtPath:fromFile toPath:toFile error:nil];
  23.    }
  24.    
  25.    // 5.GCD 迭代剪切文件
  26.    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  27.        dispatch_apply(fileArray.count, dispatch_get_global_queue(0, 0), ^(size_t index) {
  28.            
  29.            // 拼接源文件路径
  30.            NSString *fromFile = [fromPath stringByAppendingPathComponent:fileArray[index]];
  31.            // 拼接目的文件路径
  32.            NSString *toFile = [toPath stringByAppendingPathComponent:fileArray[index]];
  33.            
  34.            [mgr moveItemAtPath:toFile toPath:fromFile error:nil];
  35.            
  36.        });
  37.    });
  38. }


6> 各种队列的执行效果


并发队列

手动创建的串行队列

主队列

同步(sync)

没有开启新线程

串行执行任务

没有开启新线程

串行执行任务

没有开启新线程

串行执行任务

异步(async)

有开启新线程

并发执行任务

有开启新线程

串行执行任务

没有开启新线程

串行执行任务

注意

p使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列


7> 队列组

  • 创建队列

  1. // 1.创建队列
  2. dispatch_queue_t queue = dispatch_queue_create("com.xfsrn.www", DISPATCH_QUEUE_CONCURRENT);
  • 队列组的创建

  1. // 2.创建组
  2. dispatch_group_t group = dispatch_group_create();
  • 创建异步函数下载图片1

  1. dispatch_group_async(group, queue, ^{
  2.       // 3.1 加载URl
  3.        NSURL *url = [NSURL URLWithString:@"http://tb2.bdstatic.com/tb/static-puser/widget/celebrity/img/single_member_100_0b51e9e.png"];
  4.        // 3.2 加载二进制
  5.        NSData *data = [NSData dataWithContentsOfURL:url];
  6.        
  7.        // 3.3 二进制转为图片
  8.        self.image1 = [UIImage imageWithData:data];
  9.        
  10.    });
  • 创建异步函数下载图片2

  1. // 4.创建异步函数下载图片2
  2.    dispatch_group_async(group, queue, ^{
  3.        // 3.1 加载URl
  4.        NSURL *url = [NSURL URLWithString:@"http://tb2.bdstatic.com/tb/static-puser/widget/celebrity/img/single_member_100_0b51e9e.png"];
  5.        // 3.2 加载二进制
  6.        NSData *data = [NSData dataWithContentsOfURL:url];
  7.        
  8.        // 3.3 二进制转为图片
  9.        self.image2 = [UIImage imageWithData:data];
  10.        
  11.    });
  • 合并图片 (dispatch_group_notify 方法会等待group组中的任务执行完之后再执行此线程中的任务)

  1. // 5.合并图片 (dispatch_group_notify 方法会等待group组中的任务执行完之后再执行此线程中的任务)
  2. dispatch_group_notify(group, queue, ^{
  3. // 5.1 创建上下文
  4. UIGraphicsBeginImageContext(CGSizeMake(200, 400));
  5. // 5.2 绘制图片1
  6. [self.image1 drawInRect:CGRectMake(0, 0, 200, 200)];
  7. // 5.3 绘制图片2
  8. [self.image2 drawInRect:CGRectMake(0, 200, 200, 200)];
  9. // 5.4 从上下文种获取图片
  10. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  11. // 5.5 关闭上下文,销毁图片1和图片2
  12. UIGraphicsEndImageContext();
  13. self.image1 = nil;
  14. self.image2 = nil;
  15. // 5.6 刷新图片
  16. dispatch_async(dispatch_get_main_queue(), ^{
  17. self.imageView.image = image;
  18. });
  19. });

5. NSOperation

    NSOperation并不具备创建线程的能力,但它的两个子类可以创建线程

    子类:

    NSInvocationOperation

    NSBlockOperation

    队列:

    NSOpreationQueue


1. NSInvocationOperation的创建,在主线程中执行任务需要手动启动

  1. NSInvocationOperation *p1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(running) object:nil];
  2. [p1 start];
2. NSBlockOperation
  1. // 2.NSBlockOperation : 直接创建的任务在主线程中执行, 追加的任务在子线程中执行, 需手动开启start
  2. NSBlockOperation *p2 = [NSBlockOperation blockOperationWithBlock:^{
  3. NSLog(@"+++++++++%@", [NSThread currentThread]);
  4. }];
  5. // 追加任务
  6. [p2 addExecutionBlock:^{
  7. NSLog(@"1++++++++++%@", [NSThread currentThread]);
  8. }];
  9. [p2 start];

3. 操作和队列的应用

. 创建队列

. 创建操作

. 将操作添加到队列中

  1. // 创建队列
  2. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  3. // 创建操作
  4. NSBlockOperation *p1 = [NSBlockOperation blockOperationWithBlock:^{
  5. for (int i = 0; i < 10000; ++i) {
  6. NSLog(@"4----%d-----%@", i, [NSThread currentThread]);
  7. }
  8. }];
  9. // 将操作添加到队列中
  10. // 向队列中添加任务
  11. [queue addOperation:p1];

4. 队列分为主队列和非主队列

  • 主队列中的操作都在主线中执行

  1. NSOperationQueue *queue = [NSOperationQueue mainQueue];
  • 非主队列,通过alloc创建,具备并发和串行创建子线程能力,默认是并发,

  1. NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
  • 设置并发数,设置为1为串行

  1. // 设置并发数, 如果值设置为1即为串行执行
  2. queue.maxConcurrentOperationCount = 2;
  • 队列暂停,YES为暂停,正在执行的任务无法暂停,只能暂停未执行的操作

  1. self.queue.suspended = YES;
  • 队列取消,正在执行的任务无法取消,只能取消未执行的操作(取消之后无法再重新执行)

  1. // 取消队列 任务
  2. [self.queue cancelAllOperations];

5. 自定义NSOpreation

创建一个类继承于NSOpreation,用这个类创建NSOpreation对象,添加到队列中,将任务封装在NSOpreation的main对象函数中,就可以在子线程中执行

  1. - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
  2. {
  3. LDOperation *q = [[LDOperation alloc] init];
  4. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  5. [queue addOperation:q];
  6. }
  1. #import "LDOperation.h"
  2. @implementation LDOperation
  3. - (void)main
  4. {
  5. NSLog(@"---------main--------%@", [NSThread currentThread]);
  6. }
  7. @end


6. 设置任务的依赖关系

创建多个操作p1/p2/p3/p4, 可设定p2操作在p4操作完成之后再执行

  1. // 设置依赖关系
  2. [p2 addDependency:p4];

7. NSOpreadtion线程间的通信

  1. - (void)downloadOneImage
  2. {
  3. // 创建队列
  4. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  5. // 封装任务
  6. [queue addOperationWithBlock:^{
  7. // 获取图片URL
  8. NSURL *url = [NSURL URLWithString:@"http://img4.duitang.com/uploads/blog/201310/18/20131018212924_ZXZLs.thumb.700_0.jpeg"];
  9. // 加载二进制
  10. NSData *data = [NSData dataWithContentsOfURL:url];
  11. // 将二进制转为图片
  12. UIImage *image = [UIImage imageWithData:data];
  13. // 刷新图片
  14. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  15. NSLog(@"%@", [NSThread currentThread]);
  16. self.imageView.image = image;
  17. }];
  18. }];
  19. }

8. 在NSOpreation创建的线程中合并图片

  1. - (void)downloadTwoImage1
  2. {
  3. // 创建队列
  4. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  5. // 封装任务
  6. [queue addOperationWithBlock:^{
  7. /* ------------获取图片1---------------- */
  8. // 获取图片1URL
  9. NSURL *url = [NSURL URLWithString:@"http://if.topit.me/f/53/3f/1104263230b173f53fl.jpg"];
  10. // 加载二进制
  11. NSData *data = [NSData dataWithContentsOfURL:url];
  12. // 将二进制转为图片
  13. UIImage *image1 = [UIImage imageWithData:data];
  14. /* ------------获取图片2---------------- */
  15. // 获取图片2
  16. url = [NSURL URLWithString:@"http://imgsrc.baidu.com/forum/w%3D580/sign=2daebc56d8b44aed594ebeec831d876a/59ee3d6d55fbb2fbe8a0e70e4d4a20a44623dc27.jpg"];
  17. data = [NSData dataWithContentsOfURL:url];
  18. UIImage *image2 = [UIImage imageWithData:data];
  19. /* ------------合并图片---------------- */
  20. // 开启上下文
  21. UIGraphicsBeginImageContext(CGSizeMake(300, 150));
  22. // 绘制图片1
  23. [image1 drawInRect:CGRectMake(0, 0, 150, 150)];
  24. // 绘制图片2
  25. [image2 drawInRect:CGRectMake(150, 0, 150, 150)];
  26. // 获取上下文中图片
  27. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  28. UIGraphicsEndImageContext();
  29. // 刷新图片
  30. [[NSOperationQueue mainQueue] addOperationWithBlock:^{
  31. NSLog(@"%@", [NSThread currentThread]);
  32. self.imageView.image = image;
  33. }];
  34. }];
  35. }

通过依赖关系

  1. - (void)downloadTwoImage2
  2. {
  3. // 创建队列
  4. NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  5. NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
  6. // 封装任务
  7. NSBlockOperation *p1 = [NSBlockOperation blockOperationWithBlock:^{
  8. /* ------------获取图片1---------------- */
  9. // 获取图片1URL
  10. NSURL *url = [NSURL URLWithString:@"http://if.topit.me/f/53/3f/1104263230b173f53fl.jpg"];
  11. // 加载二进制
  12. NSData *data = [NSData dataWithContentsOfURL:url];
  13. // 将二进制转为图片
  14. UIImage *image1 = [UIImage imageWithData:data];
  15. self.image1 = image1;
  16. }];
  17. NSBlockOperation *p2 = [NSBlockOperation blockOperationWithBlock:^{
  18. /* ------------获取图片2---------------- */
  19. // 获取图片2
  20. NSURL *url = [NSURL URLWithString:@"http://imgsrc.baidu.com/forum/w%3D580/sign=2daebc56d8b44aed594ebeec831d876a/59ee3d6d55fbb2fbe8a0e70e4d4a20a44623dc27.jpg"];
  21. NSData *data = [NSData dataWithContentsOfURL:url];
  22. UIImage *image2 = [UIImage imageWithData:data];
  23. self.image2 = image2;
  24. }];
  25. NSBlockOperation *p3 = [NSBlockOperation blockOperationWithBlock:^{
  26. /* ------------合并图片---------------- */
  27. // 开启上下文
  28. UIGraphicsBeginImageContext(CGSizeMake(300, 150));
  29. // 绘制图片1
  30. [self.image1 drawInRect:CGRectMake(0, 0, 150, 150)];
  31. // 绘制图片2
  32. [self.image2 drawInRect:CGRectMake(150, 0, 150, 150)];
  33. // 获取上下文中图片
  34. UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  35. UIGraphicsEndImageContext();
  36. self.image1 = nil;
  37. self.image2 = nil;
  38. self.imageView.image = image;
  39. }];
  40. // 设置执行顺序
  41. [p3 addDependency:p1];
  42. [p3 addDependency:p2];
  43. // 任务添加到队列
  44. [queue addOperation:p1];
  45. [queue addOperation:p2];
  46. [mainQueue addOperation:p3];
  47. }



posted @ 2015-11-27 12:36  文刂Rn  阅读(285)  评论(0编辑  收藏  举报