美文网首页
线程的讲解

线程的讲解

作者: super_2e20 | 来源:发表于2018-03-27 19:43 被阅读0次

    1 -NSthread 讲解

    每个iOS应用程序都有个专门用来更新显示UI界面和处理触摸事件的主线程,为了防止UI主线程的卡顿,影响用户的体验,我们将耗时的操作放到子线程中,多线程防止主线程的堵塞,增加运行效率的最佳方法。

    NTThread:是三种方法里相对轻量级的,但需要管理线程生命周期、同步加锁问题 ,这会导致性能消耗问题

    cocoa Operations:是基于OC实现的 NSoperation以前面向对象的方式封装了需要执行的操作,不必关心线程管理、同问题.NSOperation是一个抽象基类,iOS提供了两种默认实现:NSInvocationOperation和NSBlockOperation,
    当然也可以自定义NSOperation

    Grand Central Dispatch (简称GCD):提供了一些新特性,运行库来支持多核并行编程,他关注点更高,如何在多个CPU上提升效率

    NSthread的初始化

    1.动态方法

    - (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;  
    

    //初始化线程

     NSThread  *thread = [NSThread alloc] initW�IthTarget:self  selector:@selector(run) object:nil];  
    

    // 设置线程的优先级(0.0 - 1.0,1.0最高级)

     thread.threadPriority = 1;  
    

    // 开启线程

      [thread start];  
    

    2.静态方法

    + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;  
    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];  
    

    // 调用完毕后,会马上创建并开启新线程

    3.瘾式创建线程方法

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

    二、获取当前线程

       NSThread *current = [NSThread currentThread];
    

    三、获取主线程

       NSthread *main = [NSThread mainThread];
    

    四、暂停当前线程

      [NSThread sleepForTimeInterval:2];
     NSDate *date = [NSDate dateWithTimeInterval:2  sinceDate:[NSDate date]];
     [NSThread sleepUntilDate:date];
    

    五.线程间通信

    1 .在指定线程上执行操作

     [self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];  
    

    2.在主线程上执行操作

    [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES]; 
    

    3.在当前线程执行操作

    [self performSelector:@selector(run) withObject:nil];  
    

    六.优缺点
    1.优点:NSthread 比其他两种多线程方案较轻量级,更直观底控制线程对象
    2.缺点: 需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销

    2 -NSOperation

    一.NSOperation
    1.简介

     NSOperation 实例封装了需要执行的操作和执行操作所需的数据,并且能够以并发或肺病发操作
     NSOperation 本身是抽象类,因此必须使用它的子类,使用NSOperation子类的方法:
     1>Foundation 框架提供了两个具体子类直接供我们使用:NSInvocationOperation和NSBlockOperation
     2>自定义子类继承NSOperation,实现内部相应的方法
    

    2.执行操作

      NSOperation调用start方法即可开始执行操作,NSOperation对象默认按同步方式执行,也就是在调用start方法的那个线程中直接执行。NSOperation对象的isConcurrent方法会告诉我们这个操作相对于调用start方法的线程,是同步还是异步执行。isConcurrent方法默认返回NO,表示操作与调用线程同步执行
    

    3.取消操作

      operation 开始执行之后,默认会一直执行操作指导完成,我们也可以调用cancel 方法中途取消操作
      [operation cancel];
    

    4.监听操作的执行

     operation.completionBlock =  ^(){
    
     NSLog (@"执行完毕");
    };
    

    二. NSInvocationOperation
    1.简介

    基于一个对象和selector 来创建操作。如果你已经有现有的方法来执行需要的任务,就可以使用这个类
    

    2.创建并执行操作

       // 这个操作是:调用self的run方法  
       NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];  
       // 开始执行任务(同步执行)  
       [operation start];  
    

    三、NSBlockOperation
    1.简介

    能够并发地执行一个或多个block对象,多有相关的block都执行之后,操作才算完成

    2.创建并执行操作

      NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){  
        NSLog(@"执行了一个新的操作,线程:%@", [NSThread currentThread]);  
      }];  
      // 开始执行任务(这里还是同步执行)  
       [operation start];  
    

    3.通过addExecutionBlock方法添加block操作

      NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^(){  
    NSLog(@"执行第1次操作,线程:%@", [NSThread currentThread]);  
    }];  
    
     [operation addExecutionBlock:^() {  
    NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);  
     }];  
    
     [operation addExecutionBlock:^() {  
    NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);  
    }];  
    
    [operation addExecutionBlock:^() {  
    NSLog(@"又执行了1个新的操作,线程:%@", [NSThread currentThread]);  
     }];  
    
    // 开始执行任务  
     [ operation start];  
    

    四、自定义NSOperation
    1.简介

      如果NSInvocationOperation和NSBlockOperation对象不能满足需求, 你可以直接继承NSOperation, 并添加任何你想要的行为。继承所需的工作量主要取决于你要实现非并发还是并发的NSOperation。定义非并发的NSOperation要简单许多,只需要重载-(void)main这个方法,在这个方法里面执行主任务,并正确地响应取消事件; 对于并发NSOperation, 你必须重写NSOperation的多个基本方法进行实现(这里暂时先介绍非并发的NSOperation)
    

    2、非并发的NSOperation

    比如叫做DownloadOperation,用来下载图片
    1>继承NSOperation,重写main方法,执行主任务
    

    DownloadOperation.h

       #import <Foundation/Foundation.h>  
      @protocol DownloadOperationDelegate;  
    
    @interface DownloadOperation : NSOperation  
      // 图片的url路径  
    @property (nonatomic, copy) NSString *imageUrl;  
      // 代理  
    @property (nonatomic, retain) id<DownloadOperationDelegate> delegate;  
    
    - (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate;  
    @end  
    
      // 图片下载的协议  
     @protocol DownloadOperationDelegate <NSObject>  
     - (void)downloadFinishWithImage:(UIImage *)image;  
     @end  
    

    DownloadOperation.m

     #import "DownloadOperation.h"  
    
    @implementation DownloadOperation  
    @synthesize delegate = _delegate;  
    @synthesize imageUrl = _imageUrl;  
    
     // 初始化  
     - (id)initWithUrl:(NSString *)url delegate:(id<DownloadOperationDelegate>)delegate {  
       if (self = [super init]) {  
        self.imageUrl = url;  
        self.delegate = delegate;  
    }  
    return self;  
    }  
    // 释放内存  
    - (void)dealloc {  
     [super dealloc];  
    [_delegate release];  
    [_imageUrl release];  
    }  
    
     // 执行主任务  
    - (void)main {  
     // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池  
     @autoreleasepool {  
        // ....  
       }  
    }  
    @end
    

    2>正确响应取消事件

     operation开始执行之后,会一直执行任务直到完成,或者显式地取消操作。取消可能发生在任何时候,甚至在operation执行之前。尽管NSOperation提供了一个方法,让应用取消一个操作,但是识别出取消事件则是我们自己的事情。如果operation直接终止, 可能无法回收所有已分配的内存或资源。因此operation对象需要检测取消事件,并优雅地退出执行
    
    NSOperation对象需要定期地调用isCancelled方法检测操作是否已经被取消,如果返回YES(表示已取消),则立即退出执行。不管是自定义NSOperation子类,还是使用系统提供的两个具体子类,都需要支持取消。isCancelled方法本身非常轻量,可以频繁地调用而不产生大的性能损失
    
    以下地方可能需要调用isCancelled:
    * 在执行任何实际的工作之前
    * 在循环的每次迭代过程中,如果每个迭代相对较长可能需要调用多次
    * 代码中相对比较容易中止操作的任何地方
    

    DownloadOperation的main方法实现如下

     - (void)main {  
                  // 新建一个自动释放池,如果是异步执行操作,那么将无法访问到主线程的自动释放池  
         @autoreleasepool {  
        if (self.isCancelled) return;  
          
        // 获取图片数据  
        NSURL *url = [NSURL URLWithString:self.imageUrl];  
        NSData *imageData = [NSData dataWithContentsOfURL:url];  
          
        if (self.isCancelled) {  
            url = nil;  
            imageData = nil;  
            return;  
        }  
          
        // 初始化图片  
        UIImage *image = [UIImage imageWithData:imageData];  
          
        if (self.isCancelled) {  
            image = nil;  
            return;  
        }  
          
        if ([self.delegate respondsToSelector:@selector(downloadFinishWithImage:)]) {  
            // 把图片数据传回到主线程  
            [(NSObject *)self.delegate performSelectorOnMainThread:@selector(downloadFinishWithImage:) withObject:image waitUntilDone:NO];  
            }  
         }  
      }  
    

    四、NSOperationQueue

    1、简介
    一个NSOperation对象可以通过调用start方法来执行任务,默认是同步执行的。也可以将NSOperation添加到一个NSOperationQueue(操作队列)中去执行,而且是异步执行的。
    

    创建一个操作队列

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];  
    
    2、添加NSOperation到NSOperationQueue中

    1>.添加一个operation

         [queue addOperation:operation];  
    

    2>.添加一组operation

           [queue addOperations:operations waitUntilFinished:NO];  
    

    3>.添加一个block形式的operation

        [queue addOperationWithBlock:^() {  
            NSLog(@"执行一个新的操作,线程:%@", [NSThread currentThread]);  
     }];
    

    注意:
    NSOperation添加到queue之后,绝对不要再修改NSOperation对象的状态。因为NSOperation对象可能会在任何时候运行,因此改变NSOperation对象的依赖或数据会产生不利的影响。你只能查看NSOperation对象的状态, 比如是否正在运行、等待运行、已经完成等

    3、添加NSOPeration的依赖对象

    1>当某个NSOperation对象依赖于其他NSOPeration对象完成时,就可以通过addDependency方法添加一个或多个依赖对象,只有所有依赖的对象都已经完成操作,当前NSOPeration对象才会开始执行操作。另外,通过removeDependency方法来删除依赖对象。

    [operation addDependency:operation1];

    4、修改Operation的执行顺序

    对于添加到queue中的Operations,它们的执行顺序取决于2点:
    1>首先看看NSOPeration是否已经准备好:是否准备好由对象依赖关系确定
    2>然后再根据所有NSOPeration的相对优先级来确定。优先级等级则是Operation对象本身的一个属性,默认所有Operation都拥有“普通” “优先级”,不过可以通过setQueuePriority:方法来提高或降低Operation对象的优先级。优先级只能应用于相同的queue中的Operations,如果应用有多个operation queue,每个queue的优先级等级是互相独立的。因此不同queue中的低优先级操作仍然可能比高优先级操作更早执行。

    注意:优先级不能替代依赖关系,优先级只是对已经准备好的 operations确定执行顺序。先满足依赖关系,然后再根据优先级从所有准备好的操作中选择优先级最高的那个执行。

    5、设置队列的最大并发操作数量

    虽然NSOperationQueue类设计用于并发执行Operations,你也可以强制单个queue一次只能执行一个Operation。setMaxConcurrentOperationCount:方法可以配置queue的最大并发操作数量。设为1就表示queue每次只能执行一个操作。不过operation执行的顺序仍然依赖于其它因素,比如operation是否准备好和operation的优先级等。因此串行化的operation queue并不等同于[GCD]

      // 每次只能执行一个操作  
     queue.maxConcurrentOperationCount = 1;  
     // 或者这样写  
     [queue setMaxConcurrentOperationCount:1];
    
    6、取消Operations

    一旦添加到operation queue,queue就拥有了这个Operation对象并且不能被删除,唯一能做的事情是取消。你可以调用Operation对象的cancel方法取消单个操作,也可以调用operation queue的cancelAllOperations方法取消当前queue中的所有操作。

    // 取消单个操作  
    [operation cancel];  
    // 取消queue中所有的操作  
    [queue cancelAllOperations];  
    
    7.等待Options完成

    为了最佳性能,不应该设计你的应用尽可能地异步操作,让应用在Operation正在执行时可以去处理其他事情。如果需要在当前线程中处理Operation完成后的结果,可以使用NSOPeration的waitUntilFinished方法阻塞当前线程。等待Operation完成。通常我们应该避免编写这样的代码,阻塞当前线程可能是一种简便的解决方案,但是它引入了更多的串行代码,限制了整个应用的并发性,同时也降低了用户体验。绝对不要再应用主线程中等待一个Operation,只能在第二次 或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。

    // 会阻塞当前线程,等到某个operation执行完毕
    [operation waitUntilFinished];

    除了等待单个Operation完成,你也可以同时等待一个queue中的所有操作,使用NSOperationQueue 的waitUntilAllOperationsAreFinished方法。注意:在等待一个queue时,应用的期房线程仍然可以往queue中添加Operation,因此可能会加长线程的等待时间。

    // 阻塞当前线程,等待queue的所有操作执行完毕  
    [queue waitUntilAllOperationsAreFinished];
    
    8、暂停和继续queue

    如果你想临时暂停Operations的执行,可以使用queue的setSuspended:方法暂停queue。不过暂停一个queue不会导致正在执行的operation在任务中途暂停,只是简单地阻止调度新Operation执行。你可以在响应用户请求时,暂停一个queue来暂停等待中的任务。稍后根据用户的请求,可以再次调用setSuspended:方法继续queue中operation的执行

    // 暂停queue  
    [queue setSuspended:YES];  
    
    // 继续queue  
    [queue setSuspended:NO];  

    相关文章

      网友评论

          本文标题:线程的讲解

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