iOS-四类多线程开发

作者: Jerry在种草 | 来源:发表于2016-07-23 17:23 被阅读64次

    前言

    iOS开发中,多线程的使用其实已经被简化得很容易了。使用得最多的应该是GCD,一个函数就可以开线程,往队列中加任务。不过实际的工程开发中,大家涉及到的如果是界面开发的话,对于线程的认识就只停留在这一步了,仅仅是为了不阻塞主线程而开另一个线程来做一些耗时操作。
    但是如果进行了一些网络方面的开发,比如网络下载,文件上传,则需要对多线程有深入一些的理解,至少会使用一些互斥锁来防止多个线程重复修改临界资源。
    本文旨在罗列iOS中各类多线程编程的知识。其中GCD和NSOperation我使用得最熟悉。

    四种方法

    • pthread
      linux底层接口,可以各平台通用,但是需要手动管理线程的生命周期,使用起来难度高
    • NSThread
      使用最多的是peformSelector以及currentThread
    • GCD
      基于底层接口的封装,不过还是C语言的接口。动不动就使用,任务和队列是重点。
    • NSOperation & NSOperationQueue
      基于GCD的封装,相比GCD有个特别明显的优点,那就是可以取消当前的操作,GCD是没办法取消的。

    NSThread

    初始化方法:

    // 初始化线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(openThread:) object:nil];
    // 开启线程
    [thread start];
    

    这时会调用openThread方法,这个方法就运行在thread这个线程中。

    - (void)openThread:(id *)something {
    NSThread *current = [NSThread currentThread];
    NSLog(@"执行了openThread:方法,当前线程:%@", current);
    }
    

    还有其他开启线程的方法,比如
    类方法开启

    + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
    

    隐式创建

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

    阻塞当前线程

    [NSThread sleepForTimeInterval:2];
    

    在指定线程上执行操作

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

    NSThread的开线程方式非常简单易用,系统已经帮我们省去了线程的创建、管理,销毁,我们只需要把注意力放在selector上,把指定任务的代码写完即可。

    GCD

    最重要的两个概念就是task和queue,任务和队列。你需要做的每一件事情就是一个任务。队列的存在让你可以执行多个任务。队列分串行队列和并行队列,串行就是每个任务按照先入先出挨个做,并行则不分先后一起做。
    全局并发队列

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    

    主队列(串行)

    dispatch_queue_t queue = dispatch_get_main_queue();
    

    创建队列,第一个参数为队列名,第二个参数DISPATCH_QUEUE_SERIAL串行或DISPATCH_QUEUE_CONCURRENT并行

    dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
    

    任务:同步执行
    注意queue可以是串行或并行队列

    dispatch_sync(queue, ^{
          //code here
          NSLog(@"%@", [NSThread currentThread]);
      });
    

    任务:异步执行
    注意queue可以是串行或并行队列

    dispatch_async(queue, ^{
          //code here
          NSLog(@"%@", [NSThread currentThread]);
      });
    

    NSOperation

    • NSInvocationOperation
      常见例子:initWithTarget: selector: object:,相当于performSelectorInBackground: withObject:
    • NSBlockOperation
      常见方法:blockOperationWithBlock:,addExecutionBlock:(这货竟然能并发)
    • 自定义Operation
      定制化的Operation,需要继承NSOperation以及覆写executing和finished

    invocation和block的用法都非常简单,记住[operation start]方法会让operation同步执行即可,如果要让其异步执行可自己开线程或者加入到NSOperationQueue中。
    对于自定义一个Operation,这里可以仔细说明一下。如果你的Operation不用并发,那覆写(override, 下同)一个main函数和初始化函数就可以了。
    记得最开始写的时候,一直不明白,在并发Operation的情况下,为什么要覆写executing和finished,还要覆写main或者start函数。查过了那么多的文档,总算在官方文档找到。

    需要覆写的方法.png
    • 覆写start是因为,自定义的Operation需要手动调用其start方法来开始执行这个operation。所以你必须覆写这个方法以致于替换掉原来的执行环境。还有记住不能调用[super start]
    • main方法是可选的,作用和start方法类似,如果不实现start方法,默认调用的是main。
    • executing和finish,operation有义务让调用者知道其执行的状态,所以操作执行的状态需要手动设置。
    • isConcurrent/isAsychronous 鉴别此operation是否为并发操作
      自定义Operation的代码这里就不给出来了,总之要自定义并发Operation的话就必须覆写上述几个方法。

    NSOperationQueue

    NSOperationQueue是设计来做NSOperation的并发操作用的,只要你调用了下面三个方法中的一个,你的Operation都会自动并发执行。

    [aQueue addOperation:anOp]; 
    [aQueue addOperations:anArrayOfOps waitUntilFinished:NO]; 
    [aQueue addOperationWithBlock:^{ }];
    

    需要留意的地方是,NSOperationQueue的cancelAllOperations方法,一旦调用,则会默认调用NSOperation的cancel,然后将其内部属性cancelled置为YES,如果这个Operation还未执行,那么有可能就被取消,而如果这个Operation正在执行中,那能不能取消,则结果未知。

    相关文章

      网友评论

        本文标题:iOS-四类多线程开发

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