美文网首页iOS 成长之路
iOS 开发之多线程技术使用与理解

iOS 开发之多线程技术使用与理解

作者: 奋拓达 | 来源:发表于2018-05-24 20:05 被阅读66次

    iOS多线程开发

    一、pthread创建与使用

    pthread是纯C语言实现的,属于跨平台的

    使用前需要先导入头文件 #import pthread.h

    定义一个函数
    void *run(void *param)
    {
        NSString *str = (__bridge NSString *)(param);
        // 耗时操作放在这里执行
        for (int i = 0; i < 20000; i++) {
            NSLog(@"%@----%@", [NSThread currentThread], str);
        }
        return NULL;
    }
    
    使用pthread创建线程
    - (void)test
    {
        // 声明一个线程变量
        pthread_t threadId;
        /*
         参数:
         1. 要开的线程的变量
         2. 线程的属性
         3. 要在这个子线程执行的函数(任务)
         4. 这个函数(任务)需要传递的参数
         */
        
        id str = @"hello";
    
        pthread_create(&threadId, NULL, run, (__bridge void *)(str));
    }
    

    二、NSThread线程

    NSThread的三种创建方式

    1.隐式创建的方法

    [self performSelectorInBackground:@selector(run:) withObject:@"cy"];
    

    2.静态创建的方法

    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"hello"];
    

    3.实例化创建的方法

    // 实例化一个线程对像
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    // 让线程开始工作,启动线程, 在新开的线程执行run方法
    [thread start];
    
    //耗时操作
    - (void)run:(NSString *)str
    {
        for (int i = 0; i < 10; i++) {
            NSLog(@"%@--%d", [NSThread currentThread], i);
        }
    }
    
    线程的属性设置
    NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"hello"];
    threadA.name = @"thraed A";   // 为线程设置名称
    threadA.threadPriority = 0.1; // 设置优先级
    [threadA start];
    
    NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"hello"];
    threadB.name = @"thraed B";   // 为线程设置名称
    threadB.threadPriority = 1.0; // 设置优先级
    [threadB start];
    
    **注意:线程优先级,必须调用很多次的时候,才能体现出来。取值范围:(0.0~1.0),默认值:0.5
    优先级的值越大,就越优先被调用
    
    线程的状态

    新建(线程被创建)-->就绪(线程调用start方法)-->运行(任务的执行)-->阻塞(等待、或者睡眠,会返回运行状态从新执行)-->消亡(线程耗尽,或者被强制退出调用exit方法)

    // 创建一个线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    
    // 放到可调度线程池,等待被调度。 这时候是就绪状态
    [thread start];
    
    
    - (void)run
    {
        NSLog(@"%s", __func__);
       // 刚进来就睡会, 睡2秒
       // [NSThread sleepForTimeInterval:2.0];
        
       // 睡到指定的时间点
       // [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]];
        
        for (int i = 0; i < 20; i++) {
            // 满足某一个条件以后,阻塞线程的执行。 也就是让线程休息一会
            if (i == 10) {
                [NSThread sleepForTimeInterval:3.0];
            }
            
            // 一旦达到某一个条件,就强制终止线程的执行
            if (i == 15) {
                // 一旦强制终止,就在不能重新启动
                // 一旦强制终止,后面的代码都不会执行
                [NSThread exit];
            }
            
            NSLog(@"%@--- %d", [NSThread currentThread], i);
        }
        
        NSLog(@"线程结束");
    }
    
    线程安全隐患
    1. 加锁,互斥锁
    2. 加锁,锁定的代码尽量少。
    3. 加锁范围内的代码, 同一时间只允许一个线程执行
    4. 互斥锁的参数:任何继承 NSObject *对象都可以。
    5. 要保证这个锁,所有的线程都能访问到, 而且是所有线程访问的是同一个锁对象
    演示代码

    @property(nonatomic, assign) int tickets;

    // 共享的资源
    self.tickets = 20;
    
    // 线程A
    NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicktes) object:nil];
    threadA.name = @"售票员A";
    [threadA start];
    // 线程B 
    NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicktes) object:nil];
    threadB.name = @"售票员B";
    [threadB start];
    
    - (void)saleTicktes
    {
        while (YES) {
        
       // 1.模拟一下延时,卖一张休息1秒
       [NSThread sleepForTimeInterval:1.0];
       // 加锁的位置可以传任意对象
       NSObject *obj = [[NSObject alloc] init];
       // 2.加锁,性能差不推荐,self指的是当前类本身
       `@synchronized(self)`{ //可任意对象
       
        //3. 判断是否还有票,
        if (self.tickets > 0) {
        
         // 4. 如果有票,就卖一张
         self.tickets--;
         NSLog(@"剩余的票数--%d--%@", self.tickets,[NSThread currentThread]);
    
          }else {
            //5.如果没有就返回
            NSLog(@"没票了");
            break;
          }
        }
      }
    }
    
    
    在主线程更新UI有什么好处?

    a.只在主线程更新UI,就不会出现多个线程同时改变 同一个UI控件</br>
    b.主线程的优先级最高,也就意味UI的更新优先级高,会让用户感觉很流畅

    线程安全的概念

    就是在多个线程同时执行的时候,能够保证资源信息的准确性.

    atomic原子属性--默认属性
    1. 原子属性就是针对多线程设计的。 原子属性实现 单(线程)写 多(线程)读
    2. 因为写的安全级别要求更高。 读的要求低一些,可以多读几次来保证数据的正确性</br>
    3. 如果同时重写了setter和getter方法,"_成员变量" 就不会提供</br>
    4. 可以使用 @synthesize 合成指令,告诉编译器属性的成员变量的名称
    自旋锁和互斥锁
    1. 共同点: 都可以锁定一段代码。 同一时间, 只有线程能够执行这段锁定的代码
    1. 区别:互斥锁,在锁定的时候,其他线程会睡眠,等待条件满足,再唤醒自旋锁,在锁定的时候, 其他的线程会做死循环,一直等待这条件满足,满足后立马去执行,少了一个唤醒过程

    三、GCD开发

    1.主线程间的通信

    开启子线程调用下载图片方法

    [self performSelectorInBackground:@selector(downloadImage) withObject:nil];
    
    由子线程反回到主线程的三种方式

    1.方式一

    [self performSelectorOnMainThread:@selector(downloadFinish:) withObject:image waitUntilDone:NO];
    

    2.方式二

    [self performSelector:@selector(downloadFinish:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
    

    3.方式三

    [self.iconView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
    
    开启下载任务
    //1.url, 确定一个网络上的资源路径
    NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/image/pic/item/5366d0160924ab1828b7c95336fae6cd7b890b34.jpg"];
    
    //2.通过url可以下载对应的网络资源, 网络资源传输的都是二进制 
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    //3.二进制转成图片
    UIImage *image = [UIImage imageWithData:data];
    
    //4.下载完成回到主线程中更新图片
    [self performSelectorOnMainThread:@selector(downloadFinish:) withObject:image waitUntilDone:NO];
    
    下载完成回到主线程刷新UI
    - (void)downloadFinish:(UIImage *)image
    {
        NSLog(@"%s---%@", __func__, [NSThread currentThread]);
        self.iconView.image = image;
    }
    
    总结:子线程里面的runloop默认不开启, 也就意味不会自动创建自动释放池, 子线程里面autorelease的对象 就会没有池子可放。 也就意味后面没办法进行释放。 造成内存泄露

    2.GCD的使用

    核心概念:
    1. 任务:block
    2. 队列:把任务放到队列里面,队列先进先出的原则
    3. 串行队列:顺序,一个一个执行(必须一个任务执行完了,才能从队列里面取出下一个任务)
    4. 并发队列:同时,同时执行很多个任务(可以同时取出很多个任务,只要有线程去执行)
    5. 同步sync:不会开新线程
    6. 异步async:会开新线程,多线程的代名词
    7. 串行队列同步执行:不开线程,在原来线程里面一个一个顺序执行
    8. 串行队列异步执行:开一条线程,在这个新线程里面一个一个顺序执
    9. 并发队列异步执行:开多个线程,并发执行(不一定是一个一个)执
    10. 并发队列同步执行:不开线程,在原来线程里面一个一个顺序执行
    串行队列-同步执行
    1. 串行队列:顺序,一个一个执行
    2. 同步任务:不会开辟新线程,是在当前线程执行
    3. 结果:不开新线程,在当前线程顺序执行
    4. dispatch : 调度,GCD里面函数,都是以dispatch开头的
    1. 创建一个串行队列 
    // 参数一:队列标签 参数二:队列的属性
    dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
    
    2.同步执行任务
    // 一般只要使用”同步“执行,串行队列对添加的同步任务,会立马执行
    dispatch_sync(queue, ^{
        NSLog(@"%@", [NSThread currentThread]);
    });
    
    
    串行队列-异步执行
    1. 串行队列:一个一个执行
    2. 异步执行:肯定会开新线程,在新线程执行
    3. 结果:只会开一个线程,而且所有任务都在这个新的线程里面执行
    1. 串行队列
    // 下面两种写法是一样的
    //dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
     dispatch_queue_t queue = dispatch_queue_create("itcast", NULL);
     
    // 2. 异步执行
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    并发队列-同步执行
    1. 并发队列:可以同时执行多个任务
    1. 同步任务:不会开辟新线程,是在当前线程执行
    2. 结果:不开新线程,顺序一个一个执行。
    //1. 并行队列
    dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
        
    // 2. 同步执行任务
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    并发队列-异步执行
    1. 并发队列:可以同时执行多个任务
    1. 异步执行:会开新线程,在新线程执行
    2. 结果:会开很多个线程,同时执行
    //1. 并行队列
    dispatch_queue_t queue = dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
        
    // 2. 异步执行任务
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    主队列-异步执行
    1. 主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
    2. 异步执行: 会开新线程,在新线程执行
    3. 结果: 不开线程, 只能在主线程上面,顺序执行!
    // 1. 获得主队列-> 程序启动,--> 至少有一个主线程-> 一开始就会创建主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
        
    NSLog(@"1----");
        
    // 2. 异步执行任务
    for (int i = 0; i < 10; i++) {
        NSLog(@"调度前---");
        // 异步:把任务放到主队列里,但是不需要马上执行
        dispatch_async(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
        NSLog(@"睡会");
        [NSThread sleepForTimeInterval:2.0];
    }
    NSLog(@"完成----");
    
    主队列-同步执行
    1. 主队列:专门负责在主线程上调度任务,不会在子线程调度任务,在主队列不允许开新线程.
    2. 同步执行:要马上执行
    3. 结果:死锁
    1. 获得主队列-> 程序启动,--> 至少有一个主线程-> 一开始就会创建主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2. 同步执行任务
    for (int i = 0; i < 10; i++) {
        NSLog(@"调度前---");
        // 同步:把任务放到主队列里,但需是马上执行
        dispatch_sync(queue, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
        NSLog(@"睡会");
        [NSThread sleepForTimeInterval:2.0];
    }
    
    
    全局队列-同步执行

    主队列:如果主队列正在执行代码,就不调度任务<BR>
    同步执行:如果第一个任务没有执行,就继续等待第一个任务执行完毕,再执行下一个任务此时相互等待,程序无法往下执行(死锁)代码从上往下执行,相互等待产生死锁

    for (int i = 0; i < 10; i++) {
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"hello :%@ %d",[NSThread currentThread],i);
        });
    }
    

    主队列同步执行(解决死锁的问题)

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 10; i++) {
            dispatch_sync(dispatch_get_main_queue(), ^{
                NSLog(@"hello :%@ %d",[NSThread currentThread],i);
            });
         }
      })
    
    全局队列-异步执行

    第一个参数: 一般写0

    第二个参数:保留参数 0

     iOS7
     DISPATCH_QUEUE_PRIORITY_HIGH 2     高优先级
     DISPATCH_QUEUE_PRIORITY_DEFAULT 0  默认优先级
     DISPATCH_QUEUE_PRIORITY_LOW (-2)   低优先级
     DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 后台优先级
      
    (可以适配iOS7 &iOS8)iOS8的质量与iOS7的对应的
     QOS_CLASS_DEFAULT  0
    
    // 获得全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 添加异步任务
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    全局队列跟并发队列的区别
    1. 全局队列没有名称 并发队列有名称
    2. 全局队列,是供所有的应用程序共享。
    3. 在MRC开发,并发队列,创建完了,需要释放。 全局队列不需要我们管理
    同步任务的作用一

    有一个小说网站->必须登录->才能下载小说

    // 1.并发队列
    dispatch_queue_t  queue = dispatch_queue_create("cy", DISPATCH_QUEUE_CONCURRENT);
    
    // 2.同步任务,需要马上执行。 不开新线程
    dispatch_sync(queue, ^{
        NSLog(@"用户登录 %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"下载小说A %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"下载小说B %@", [NSThread currentThread]);
    });
    

    控制台打印结果

    2018-03-09 00:23:52.137020+0800 05-GCD的使用[25734:836418] 用户登录 <NSThread: 0x600000075d80>{number = 1, name = main}
    2018-03-09 00:23:52.137341+0800 05-GCD的使用[25734:836691] 下载小说B <NSThread: 0x60000046b9c0>{number = 6, name = (null)}
    2018-03-09 00:23:52.137375+0800 05-GCD的使用[25734:836692] 下载小说A <NSThread: 0x60400046bf40>{number = 5, name = (null)}
    
    同步任务的作用二

    做三件耗时的操作:appStore->输入密码->扣费->下载应用

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // 1.全局队列-同步下载
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"输入密码: %@",[NSThread currentThread]);
        });
        // 2.全局队列-同步下载
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"扣费: %@",[NSThread currentThread]);
        });
        // 3.全局队列-异步下载
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"下载应用: %@",[NSThread currentThread]);
        });
    });
    

    控制台打印结果

    2018-03-09 00:21:09.547556+0800 05-GCD的使用[25639:833211] 输入密码: <NSThread: 0x60000027d300>{number = 3, name = (null)}
    2018-03-09 00:21:09.547794+0800 05-GCD的使用[25639:833211] 扣费: <NSThread: 0x60000027d300>{number = 3, name = (null)}
    2018-03-09 00:21:09.548115+0800 05-GCD的使用[25639:833211] 下载应用: <NSThread: 0x60000027d300>{number = 3, name = (null)}
    
    阶段性总结:
    1. 开不开线程,由执行任务方法决定,同步不开线程,异步肯定开线程</br>
    2. 开多少线程,由队列决定,串行 最多 开一个线程, 并发可以开多个线程。 具体开多少个,有GCD底层决定,程序猿不能控制
    GCD其他用法

    一次性执行

    // GCD创建的一次执行
    static dispatch_once_t onceToken;
     NSLog(@"%ld", onceToken);
     dispatch_once(&onceToken, ^{
        NSLog(@"%----ld", onceToken);
     });
    NSLog(@"完成");
    

    GCD延时操作一

     // 参数: when->表示从现在开始,经过多少纳秒以后
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
    
    dispatch_after(when, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"点我了-- %@", [NSThread currentThread]);
    });
    

    GCD延时操作二

    // GCD创建方式(延时2秒)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
    });
    // 隐式创建线程(延时2秒)
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    // 定时器方式(延时2秒)
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
    

    GCD-调度组

    // 开发的时候,有的时候出现多个网络请求都完成以后(每一个网络请求的事件长短不一定),再统一通知用户 
    // 比如:下载小说:三国演义->红楼梦->西游记->水浒传
    
    // 实例化一个调度组
    dispatch_group_t group = dispatch_group_create();
    
    // 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 任务添加到队列queue
    dispatch_group_async(group, queue, ^{
        NSLog(@"下载小说A---%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"下载小说B---%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"下载小说X---%@", [NSThread currentThread]);
    });
    
    // 在调度组完成通知里,可以跨队列通信
    dispatch_group_notify(group, queue, ^{
        NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       
        });
    });
    
    // 获得所有调度组里面的异步任务完成的通知
    /**
    dispatch_group_notify(group, queue, ^{
        NSLog(@"下载完成,请观看%@", [NSThread currentThread]); // 异步的
    });
    */
    

    GCD-Barrier阻塞用法

    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_async(queue, ^{
            NSLog(@"----1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----2-----%@", [NSThread currentThread]);
        });
            
        dispatch_barrier_async(queue, ^{
            NSLog(@"----barrier-----%@", [NSThread currentThread]);
        });
            
        dispatch_async(queue, ^{
            NSLog(@"----3-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----4-----%@", [NSThread currentThread]);
        });
        
    解释:在执行到dispatch_barrier_async以前,异步线程是无序执行的,之后是有序执行的
    

    GCD-dispatch_apply

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_apply(subpaths.count, queue, ^(size_t index) {
    
    });
    

    非GCD单例

    static id _instance;
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
        @synchronized(self) {
            if (_instance == nil) {
                _instance = [super allocWithZone:zone];
            }
        }
        return _instance;
    }
    
    + (instancetype)sharedInstance
    {
        @synchronized(self) {
            if (_instance == nil) {
                _instance = [[self alloc] init];
            }
        }
        return _instance;
    }
    
    - (id)copyWithZone:(NSZone *)zone
    {
        return _instance;
    }
    

    GCD-宏定义单例

    使用时导入其头文件和实现文件即可,名称修改成自定义
    // .h文件
    #define CYSingletonH(name) + (instancetype)shared##name;
    
    // .m文件
    #define CYSingletonM(name) \
    static id _instance; \
     \
    + (instancetype)allocWithZone:(struct _NSZone *)zone \
    { \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            _instance = [super allocWithZone:zone]; \
        }); \
        return _instance; \
    } \
     \
    + (instancetype)shared##name \
    { \
        static dispatch_once_t onceToken; \
        dispatch_once(&onceToken, ^{ \
            _instance = [[self alloc] init]; \
        }); \
        return _instance; \
    } \
     \
    - (id)copyWithZone:(NSZone *)zone \
    { \
        return _instance; \
    }
    

    四、NSOperation开发

    NSOperation的作用

    配合使用NSOperation和NSOperationQueue也能实现多线程编程

    NSOperation和NSOperationQueue实现多线程的具体步骤
    1. 先将需要执行的操作封装到一个NSOperation对象中
    2. 然后将NSOperation对象添加到NSOperationQueue中
    3. 系统会自动将NSOperationQueue中的NSOperation取出来
    4. 将取出的NSOperation封装的操作放到一条新线程中执行
    5. NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
    使用NSOperation有三种方式
    1. NSInvocationOperation
    2. NSBlockOperation
    3. 自定义子类继承NSOperation,实现内部相应的方法
    使用block创建Operation
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主线程
        NSLog(@"下载1------%@", [NSThread currentThread]);
     }];
        
    // 添加额外的任务(在子线程执行)
    [op addExecutionBlock:^{
        NSLog(@"下载2------%@", [NSThread currentThread]);
     }];
    
    [op addExecutionBlock:^{
        NSLog(@"下载3------%@", [NSThread currentThread]);
     }];
    [op addExecutionBlock:^{
        NSLog(@"下载4------%@", [NSThread currentThread]);
     }];
        
    [op start];
    
    使用invocation创建Operation
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    
    [op start];
    

    注意:

    1. 默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
    2. 只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
    NSOperationQueue

    作用:

    1. NSOperation可以调用start方法来执行任务,但默认是同步执行的
    2. 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
    添加操作到NSOperationQueue中
    - (void)addOperation:(NSOperation *)op;
    - (void)addOperationWithBlock:(void (^)(void))block;
    
    最大并发数

    概念:同时执行的任务数,比如同时开3个线程执行3个任务,并发数就是3

    最大并发数的相关方法
    - (NSInteger)maxConcurrentOperationCount;
    - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
    
    队列的取消、暂停、恢复

    取消队列的所有操作

    提示:也可以调用NSOperation的- (void)cancel方法取消单个操作

    - (void)cancel;
    - (void)cancelAllOperations;
    
    
    暂停和恢复队列
    - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
    - (BOOL)isSuspended;
    
    操作的监听

    可以监听一个操作的执行完毕

    - (void (^)(void))completionBlock;
    - (void)setCompletionBlock:(void (^)(void))block;
    
    自定义NSOperation

    自定义NSOperation的步骤很简单

    重写- (void)main方法,在里面实现想执行的任务

    重写- (void)main方法的注意点
    1. 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
    2. 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
    自定义NSOperation
    #import "CYOperation.h"
    
    @implementation CYOperation
    
    /**
     * 需要执行的任务
     */
    - (void)main
    {
        for (NSInteger i = 0; i<1000; i++) {
            NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
        }
        if (self.isCancelled) return;
        
        for (NSInteger i = 0; i<1000; i++) {
            NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
        }
        if (self.isCancelled) return;
        
        for (NSInteger i = 0; i<1000; i++) {
            NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
        }
        if (self.isCancelled) return;
    }
    
    
    - (void)operationQueue1
    {
        // 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        // 创建操作(任务)
        // 创建NSInvocationOperation
        NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
        NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
        
        // 创建NSBlockOperation
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download3 --- %@", [NSThread currentThread]);
        }];
        
        [op3 addExecutionBlock:^{
            NSLog(@"download4 --- %@", [NSThread currentThread]);
        }];
        [op3 addExecutionBlock:^{
            NSLog(@"download5 --- %@", [NSThread currentThread]);
        }];
        
        
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download6 --- %@", [NSThread currentThread]);
        }];
        
        // 创建CYOperation
        CYOperation *op5 = [[CYOperation alloc] init];
        
        // 添加任务到队列中
        [queue addOperation:op1]; // [op1 start]
        [queue addOperation:op2]; // [op2 start]
        [queue addOperation:op3]; // [op3 start]
        [queue addOperation:op4]; // [op4 start]
        [queue addOperation:op5]; // [op5 start]
    }
    
    - (void)download1
    {
        NSLog(@"download1 --- %@", [NSThread currentThread]);
    }
    
    - (void)download2
    {
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }
    
    - (void)operationQueue2
    {
        // 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        // 创建操作
        // NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        // NSLog(@"download1 --- %@", [NSThread currentThread]);
        // }];
        
        // 添加操作到队列中
        // [queue addOperation:op1];
        
        
        [queue addOperationWithBlock:^{
            NSLog(@"download1 --- %@", [NSThread currentThread]);
        }];
        [queue addOperationWithBlock:^{
            NSLog(@"download2 --- %@", [NSThread currentThread]);
        }];
    }
    
    - (void)opetationQueue3
    {
        // 创建队列
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
        // 设置最大并发操作数
        //    queue.maxConcurrentOperationCount = 2;
        queue.maxConcurrentOperationCount = 1; // 就变成了串行队列
        
        // 添加操作
        [queue addOperationWithBlock:^{
            NSLog(@"download1 --- %@", [NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.01];
        }];
        [queue addOperationWithBlock:^{
            NSLog(@"download2 --- %@", [NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.01];
        }];
        [queue addOperationWithBlock:^{
            NSLog(@"download3 --- %@", [NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.01];
        }];
        [queue addOperationWithBlock:^{
            NSLog(@"download4 --- %@", [NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.01];
        }];
        [queue addOperationWithBlock:^{
            NSLog(@"download5 --- %@", [NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.01];
        }];
        [queue addOperationWithBlock:^{
            NSLog(@"download6 --- %@", [NSThread currentThread]);
            [NSThread sleepForTimeInterval:0.01];
        }];
    }
    
    NSOperation操作依赖

    @property (weak, nonatomic) IBOutlet UIImageView *imageView;

      NSOperationQueue *queue = [[NSOperationQueue alloc] init];
      
        NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download1----%@", [NSThread  currentThread]);
        }];
        NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download2----%@", [NSThread  currentThread]);
        }];
        NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download3----%@", [NSThread  currentThread]);
        }];
        NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"download4----%@", [NSThread  currentThread]);
            }
        }];
        NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
            NSLog(@"download5----%@", [NSThread  currentThread]);
        }];
        op5.completionBlock = ^{
            NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
        };
        
        // 设置依赖
        [op3 addDependency:op1];
        [op3 addDependency:op2];
        [op3 addDependency:op4];
        
        [queue addOperation:op1];
        [queue addOperation:op2];
        [queue addOperation:op3];
        [queue addOperation:op4];
        [queue addOperation:op5];
    
    NSOperation-线程间的通信
     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        __block UIImage *image1 = nil;
        // 下载图片1
        NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
            
            // 图片的网络路径
            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 = nil;
        // 下载图片2
        NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
            
            // 图片的网络路径
            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)];
            image1 = nil;
            
            [image2 drawInRect:CGRectMake(50, 0, 50, 100)];
            image2 = nil;
            
            // 取得上下文中的图片
            UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
            
            // 结束上下文
            UIGraphicsEndImageContext();
            
            // 回到主线程
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                self.imageView.image = image;
            }];
        }];
        [combine addDependency:download1];
        [combine addDependency:download2];
        
        [queue addOperation:download1];
        [queue addOperation:download2];
        [queue addOperation:combine];
    }
    
    /**
     * 线程之间的通信
     */
    - (void)test
    {
        [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
            // 图片的网络路径
           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];
            
            // 回到主线程
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                self.imageView.image = image;
            }];
        }];
    }
    

    相关文章

      网友评论

      本文标题:iOS 开发之多线程技术使用与理解

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