多线程(归纳,总结)

作者: 蒋昉霖 | 来源:发表于2016-03-21 14:03 被阅读740次

    总结一下多线程的东西,防止忘记。。。不然每次都得翻出来重新看

    基础简介

    • 进程

    • 进程是指在系统中正在运行的一个应用程序

    • 每个进程之间是独立的,每个进程均运行在某专用且受保护的内存空间内

    • 线程

    • 1个进程要想执行任务,必须得有线程存在(每一个进程至少有一个线程)

    • 1个进程(程序)的所有任务都在线程里完成

    Snip20160321_1.png

    线程的执行是串行的,一个一个按顺序执行
    所以一般优化程序流畅度,就要开启多个线程,多个线程之间是并行的,可以分开处理不同的操作,一起执行

    • 多线程的原理

    • 同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)

    • 多线程并发(同时)执行,其实是CPU快速地在多线程之间调度(切换)

    • 线程不能太多

    • 如果线程过多,CPU调度不过来,会卡死

    • 每条线程都会执行缓慢

    • 多线程的优点

    • 能适当提高程序的执行效率

    • 能适当的提高资源利用率(CPU,内存的利用率,不会空转)

    • 多线程的缺点

    • 创建线程是有开销的,iOS主要成本包括:

      • 内核数据结构(大约1KB)
      • 栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K,不建议自己设定,系统既然设定了就是有用的),创建线程大约需要90毫秒的创建时间
      • 线程开启太多,会降低程序的性能,iOS一般最多5条线程
      • 线程越多,CPU在调度线程上的开销越大
      • 程序设计更加复杂:比如线程之间的通信,多线程的数据共享
    • 主线程

    • 一个程序运行后,默认会开启一条线程,成为"主线程"或者"UI线程"

    • 主线程的作用

    • 显示\刷新UI界面

    • 处理UI时间(比如点击事件\滚动事件\拖拽事件等)

    • 主线程的使用注意

    • 耗时操作放在子线程,不要放在主线程

    四中多线程的实现

    Snip20160321_2.png

    pthead

    纯C语言的,这玩意儿基本上不用
    创建代码

    // 第一个传一个
    pthread_create(<#pthread_t *restrict#>, <#const pthread_attr_t *restrict#>, <#void *(*)(void *)#>, <#void *restrict#>)
    第一个参数需要创建pthread_t对象地址,第三个参数传一个C语言函数,第二个和第四个参数传NULL
    
    // 例子
        pthread_t thread2;
        pthread_create(&thread2, NULL, buttonClick, NULL);
    
    void * buttonClick(void *param)
    {
        for (NSInteger i = 0; i<10000; i++) {
            NSLog(@"------buttonClick---%zd--%@", i, [NSThread currentThread]);
        }
        return NULL;
    }
    

    这个C的东西,反正我没用过...

    NSThread

    这种多线程的方法是比较轻量级,全程自己手动管理生命周期,同步,加锁等问题,封装了C语言的多线程

    • 动态方法
    /**
    * selector : 线程执行的方法,这个selector最多只能接收一个参数
    * target : selector是消息发送的对象,发给谁的
    * argument : 传给selector的唯一参数
    - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
    
    // 例子,线程的开启
    // 初始化线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    // 设置线程的优先级(0.0 ~ 1.0,1.0是最高级)
    thread.threadPriority = 1;
    // 设置线程的名字
    thread.name = @"jack";
    // 开启线程
    [thread start];
    
    • 其他创建多线程的方法
    • 静态方法
    + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
    // 例子
    // 调用完毕后,会马上创建并开启新线程  
    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];  
    
    • 隐式创建线程
    [self performSelectorInBackground:@selector(run) withObject:nil]
    

    优点: 简单快捷
    缺点:无法对线程进行更详细的设置

    相关方法

    + (NSThread *)mainThread; // 获得主线程
    - (BOOL)isMainThread; // 是否为主线程
    + (BOOL)isMainThread; // 是否为主线程
    
    获得当前线程
    NSThread *current = [NSThread currentThread];
    
    线程的名字
    - (void)setName:(NSString *)n;
    - (NSString *)name;
    
    控制线程的状态
    • 启动线程
    // 进入就绪状态->运行状态-- 执行完毕 -- > 死亡状态
    - (void)start;
    
    • 阻塞(暂停)线程
    • (void)sleepUntilDate:(NSDate *)date;
    // 例子
    //    [NSThread sleepUntilDate:[NSDate distantFuture]]; // [NSDate distantFuture]从现在开始到永远
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
    
    • (void)sleepForTimeInterval:(NSTimeInterval)ti;
        [NSThread sleepForTimeInterval:2]; // 让线程睡眠2秒(阻塞2秒)
    
    • 强制死亡状态
    // 强制停止线程
    + (void)exit;
    

    一旦线程死亡,就必须重新开启新的线程,不能重新调用

    线程锁

    在多个线程同时访问一个对象的时候,同时对该对象进行了操作,就会出现安全隐患,用NSThread的方法需要自己管理线程锁,这就是互斥锁

    • 互斥锁的使用格式
      @synchronized(锁对象) { // 需要锁定的代码 }
      // 一份代码只有一把锁,多把锁是无效的,还是乱的
      // 这个锁一般是全局变量,表示唯一的(切记)

    互斥锁的优缺点
    优点:能有效防止因多线程抢夺资源造成的数据安全问题
    缺点:需要消耗大量的CPU资源

    加互斥锁也就是要打到"线程同步"的问题
    线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
    互斥锁,就是使用了线程同步技术
    意思就是说所有线程上同一对象是一样的!!!!

    线程之间的通信

    在一个进程中,多个线程之间是有一定关联,相互通信的

    • 一般在子线程进行复杂操作,在主线程刷新UI,这就是线程通信
    Snip20160321_5.png

    常用方法

    - (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
    - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
    // 最后这个(BOOL)wait是指下面代码是否等这个执行完了之后再进行
    
    • 苹果还提供了第二种线程同步的方法NSMachPort
      NSPort有3个子类:NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。
      使用的方式为接收线程中注册NSMachPort,在另外的线程中使用此port发送消息,则被注册线程会收到相应消息,然后最终在主线程里调用某个回调函数。
      可以看到,使用NSMachPort的结果为调用了其它线程的1个函数,而这正是performSelector所做的事情,所以,NSMachPort是个鸡肋。线程间通信应该都通过performSelector来搞定。

    GCD

    Grand Central Dispatch(中央调度)
    纯C语言,对NSThread进行了封装

    切记:

    不要在当前穿行队列里使用sync函数,因为会卡住线程,调用sync函数,系统会马上去执行这个函数,但是当前线程也是串行的,他会默认执行完当前线程再去执行别的,这就造成了冲突,会卡死

    GCD优势:

    • 为多核的并发运算提出的解决方法
    • 自动利用更多的CPU内核(比如双核,四核)
    • 自动管理线程的生命周期(创建线程,调度任务,销毁线程等)
    • 只需要告诉GCD执行什么,GCD会自行管理任务

    GCD两个核心概念

    • 任务:执行什么操作
    • 队列:用来存放任务

    GCD的使用就2个步骤

    • 定制任务
    • 确定想做的事情

    将任务添加到队列中

    • GCD会自动将队列中的任务取出,放到对应的线程中执行
    • 任务的取出遵循队列的FIFO原则:先进先出,后进后出

    GCD的OC对象的内存管理
    GCD支持Cocoa内存管理机制,因此可以在提交到queue的block中自由地使用Objective-C对象。每个dispatch queue维护自己的autorelease pool确保释放autorelease对象,但是queue不保证这些对象实际释放的时间。如果应用消耗大量内存,并且创建大量autorelease对象,你需要创建自己的autorelease pool,用来及时地释放不再使用的对象。

    同步和异步的区别

    • 同步:只能在当前线程中执行任务,不具备开启线程的能力
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    
    • 异步:可以在新的线程中执行任务,具备开启线程的能力
    dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
    
    Snip20160321_7.png

    例子

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        [self syncConcurrent];
    }
    
    /**
     * 同步函数 + 主队列:
     */
    - (void)syncMain
    {
        NSLog(@"syncMain ----- begin");
        
        // 1.获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 2.将任务加入队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
        
        NSLog(@"syncMain ----- end");
    }
    
    /**
     * 异步函数 + 主队列:只在主线程中执行任务
     */
    - (void)asyncMain
    {
        // 1.获得主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 2.将任务加入队列
        dispatch_async(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
    }
    
    /**
     * 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
     */
    // 俩参数,第一个是名字,第二个是执行的优先级
    /**
    * 全局并发队列的优先级
    * #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
    * #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
    * #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
    * #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
    */
    - (void)syncSerial
    {
        // 1.创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
        
        // 2.将任务加入队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
    }
    
    /**
     * 异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务
     */
    - (void)asyncSerial
    {
        // 1.创建串行队列
        // 两个参数,第一个是名字,第二个是穿行队列
        // DISPATCH_QUEUE_CONCURRENT 并行队列
        // DISPATCH_QUEUE_SERIAL            串行队列
        dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
    //    默认是穿行队列,所以第二个参数传NULL也行
    ![Uploading Snip20160321_7_826971.png . . .]
    //    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
        
        // 2.将任务加入队列
        dispatch_async(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
    }
    
    /**
     * 同步函数 + 并发队列:不会开启新的线程
     * 因为还是同步函数,所以你的并发执行并没有什么卵用
     */
    - (void)syncConcurrent
    {
        // 1.获得全局的并发队列
        // 在你程序启动的时候,实际上GCD已经给你创建了几个全局的队列,你拿来用就好了,所以其实可以不用自己创建
        // 俩参数,第一个为线程的优先级,第二个为预留参数,传0就行了
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        // 2.将任务加入队列
        dispatch_sync(queue, ^{
            NSLog(@"1-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"2-----%@", [NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"3-----%@", [NSThread currentThread]);
        });
        
        NSLog(@"syncConcurrent--------end");
    }
    
    /**
     * 异步函数 + 并发队列:可以同时开启多条线程
     */
    - (void)asyncConcurrent
    {
        // 1.创建一个并发队列
        // label : 相当于队列的名字
        // 俩参数,第一个是线程的名字,第二个是优先级
    //    dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
        
        // 1.获得全局的并发队列
        // 在你程序启动的时候,实际上GCD已经给你创建了几个全局的队列,你拿来用就好了,所以其实可以不用自己创建
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        // 2.将任务加入队列
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"1-----%@", [NSThread currentThread]);
            }
        });
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"2-----%@", [NSThread currentThread]);
            }
        });
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i<10; i++) {
                NSLog(@"3-----%@", [NSThread currentThread]);
            }
        });
        
        NSLog(@"asyncConcurrent--------end");
    //    dispatch_release(queue);
    }
    

    GCD里面线程之间的通信

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        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_async(dispatch_get_main_queue(), ^{
                self.imageView.image = image;
            });
        });
    }
    
    GCD的其他使用
    • barrier障碍

    把几个线程分开,先执行障碍之前的代码,然后执行障碍,最后执行障碍以后的代码

        dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
    

    例子

    - (void)barrier
    {
    //    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        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_time延迟执行的代码在block里面,第一个参数为开始时间,第二个参数为延迟的秒数,单位为纳米,转化单位NSEC_PER_SEC ,第三个参数为执行的线程

        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            <#code to be executed after a specified delay#>
        });
    

    例子

    - (void)delay
    {
        NSLog(@"touchesBegan-----");
        [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"run-----");
        });
        [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
    }
    
    - (void)run
    {
        NSLog(@"run-----");
    }
    
    • 一次性代码(单例要用)
    // 一次执行代码,这个代码在这一整个进程里就执行一次,不会执行第二次
    - (void)once
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"------run");
        });
    }
    
    • 快速迭代(我感觉就是遍历)
    // 用GCD
    - (void)apply
    {
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        NSString *from = @"/Users/xiaomage/Desktop/From";
        NSString *to = @"/Users/xiaomage/Desktop/To";
        
        NSFileManager *mgr = [NSFileManager defaultManager];
        NSArray *subpaths = [mgr subpathsAtPath:from];
    
        // 快速迭代  block里面有个参数为size_t index
        dispatch_apply(subpaths.count, queue, ^(size_t index) {
            NSString *subpath = subpaths[index];
            NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
            NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
            // 剪切
            [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
            
            NSLog(@"%@---%@", [NSThread currentThread], subpath);
        });
    }
    
    /**
     * 对比:传统文件剪切
     */
    - (void)moveFile
    {
        NSString *from = @"/Users/xiaomage/Desktop/From";
        NSString *to = @"/Users/xiaomage/Desktop/To";
    
        NSFileManager *mgr = [NSFileManager defaultManager];
        NSArray *subpaths = [mgr subpathsAtPath:from];
    
        for (NSString *subpath in subpaths) {
            NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
            NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
    // 复杂耗时操作放在子线程       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                // 剪切
                [mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
            });
        }
    }
    
    • 设置依赖分组关系
    • 创建队列
    • 创建group组对象dispatch_group_create()
    • 讲操作加到组里
    • 在组里完成所有操作后,通过 dispatch_notify(<#object#>, <#queue#>, <#notification_block#>) 方法执行下一步操作,有三个参数,第一个是哪一组的,第二个是在哪个队列,第三个是要执行的方法block
    - (void)group
    {
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        // 创建一个队列组
        dispatch_group_t group = dispatch_group_create();
        
        // 1.下载图片1
        dispatch_group_async(group, queue, ^{
            // 图片的网络路径
            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];
        });
        
        // 2.下载图片2
        dispatch_group_async(group, queue, ^{
            // 图片的网络路径
            NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
            
            // 加载图片
            NSData *data = [NSData dataWithContentsOfURL:url];
            
            // 生成图片
            self.image2 = [UIImage imageWithData:data];
        });
        
        // 3.将图片1、图片2合成一张新的图片
        dispatch_group_notify(group, queue, ^{
            // 开启新的图形上下文
            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(), ^{
                // 4.将新图片显示出来 
                self.imageView.image = image;
            });
        });
    }
    

    NSOperation

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

    NSOperation和NSOperationQueue实现多线程的具体步骤

    1. 先将需要执行的操作封装到一个NSOperation对象中
    2. 然后将NSOperation对象添加到NSOperationQueue中
    3. 系统会自动将NSOperationQueue中的NSOperation取出来
    4. 将取出的NSOperation封装的操作放到一条新线程中执行

    NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类

    使用NSOperation子类的方式有3种

    • NSInvocationOperation(不常用)
    • NSBlockOperation
    • 自定义子类继承NSOperation,实现内部相应的方法
    NSInvocationOperation
    - (void)invocationOperation
    {
        // 创建NSInvocationOperation对象
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
        // 调用start方法开始执行操作
        [op start];
    }
    
    • 默认情况下,调用start方法后并不会开一条新线程去执行操作,而是在当前线程同步进行操作
    • 只有讲NSOperation放到一个NSOperationQueue中,才会异步执行操作
    NSBlockOperation

    在NSBlockOperation操作里,只要封装的任务个数大于1,就会异步执行

    - (void)blockOperation
    {
        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];
    }
    
    NSOperationQueue

    NSOperation一般是和NSOperationQueue配合使用的
    在NSOperation调用start方法的时候,如果没有加入NSOperationQueue,那么他们是同步在当前线程执行的,只有加入了NSOperationQueue,系统会自动给你异步执行,而且怎么异步,开多少线程都不用你管

    两种方式

    • (void)addOperation:(NSOperation *)op;
    • (void)addOperationWithBlock:(void (^)(void))block;
    
    示例如下
    ```objc
    - (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]);
        }];
        
        // 创建XMGOperation
        XMGOperation *op5 = [[XMGOperation 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)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]);
        }];
    }
    
    • 最大并发数
      可以设置最大并发数,也就是最多开启的线程数
      把最大并发数设置成1,就变成了串行
    - (NSInteger)maxConcurrentOperationCount;
    - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
    
    • 取消队列的所有操作
    - (void)cancelAllOperations;```
    提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
    
    - 暂停和恢复队列
    
    ```objc    
    - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
    - (BOOL)isSuspended;
    
    • 线程之间的依赖
      这个设置起来比着GCD要简单的多,GCD可以通过barrier,建组的方式,然而,NSOperation却只需要一行代码,而且还可以设置不同线程之间的依赖等
    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        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];
    }
    

    同样是下载图片的依赖关系

    - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        NSOperationQueue *queue = [[NSOperationQueue alloc] init];
        
    // 想让block里面改外面的局部变量的值,在外部参数加__block
        __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;
            }];
        }];
    }
    

    相关文章

      网友评论

      • 杰米:/** * 同步函数 + 并发队列:不会开启新的线程 * 因为还是同步函数,所以你的并发执行并没有什么卵用 */
        那么在队列里的操作都是在同一条线程里实现,还是 在不同线程里实现的同步执行?
        蒋昉霖:@杰米 同一条线程

      本文标题:多线程(归纳,总结)

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