美文网首页
iOS开发 多线程的运用

iOS开发 多线程的运用

作者: 弹一首键盘协奏曲 | 来源:发表于2020-02-20 16:27 被阅读0次

在iOS开发上搬了几年砖了,一直在向各位大神学习,这段时间公司项目完工了,整理一下相关技术点,向后来者做个借鉴,沉淀一下自己。

说起多线程基本上每个开发者都会用到,今天总结一下多线程方面的应用技巧。

总纲

1.概念相关

2.应用场景

3.相关技术和用法

1.概念相关

1.1、什么是多线程?

线程是指程序在执行过程中能够执行代码的一个执行单元,线程主要有新建、就绪、挂起、结束四种状态。

1.2、线程与进程的区别?

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序过程就称之为进程。进程是程序执行的一次活动。进程中有线程在执行,且一个进程中最少有一条线程,这条线程我们叫它为主线程(主线程中运行着runloop),主线程一般显示、刷新UI界面,处理UI事件(点击、滚动、拖拽事件等),我们也一般叫它UI线程。当然进程也也可以开多条线程,我们称之为子线程。开多条线程的使用情况我们称为多线程。

1.3、多线程的原理

同一时间内一个CPU只能处理一条线程,也只有一条线程在工作,在单核cpu情况下多线程其实就是一个cpu在多条线程之间调度,当然多核CPU才是真正意义上的多线程。CPU在多条线程调度会消耗大量的CPU资源,每条线程被调度频次会降低且消耗内存,一般情况不要同时开5条线程以上

1.4、多线程优缺点

优点:适当提高了程序的执行效率,把那些占用时长的操作放到后台处理,优化应用流畅度,增强用户体验。

缺点:大量的线程需要更多的内存。当多个线程对同一资源出现争夺时候还要注意线程安全问题

2.应用场景

一般情况下我们使用一条线程进程就能跑通了,那么既然有多线程,我们在什么情况下使用多线程呢?我们都知道主线程是UI线程, 它可以渲染UI,负责将视图呈现给我们,当这个时候如果有一个非常耗时的操作,比如说视频的加载或者图片的下载,如果网络不好的情况下可能要等好久,如果在一条线程中执行,那么我们的应用界面将出现卡死现象。这时候如果把图片下载放到子线程中,等图片下载完成再回到主线程渲染UI,那么就不会出现卡死或者卡顿现象。开发中常见的使用多线程地方有图片的下载、版本号的加载、消息红点的校对、计时器、文件的下载等

3.技术方案

目前在iOS开发中有四种技术方案,分别是Pthread、NSThread、GCD、NSOperation

3.1、Pthread

Pthread是一套通用的API,可以跨平台使用,但是使用难度大,语言是C语言,线程生命周期由程序员管理,在开发中几乎不用,当然在平常项目开发中我也没用过,只在闲暇时候demo写过简单的使用方法。

//使用时需要引入#import <pthread.h>
      //创建线程对象
        pthread_t thread = NULL;
        
        //传递的参数
        id str = @"param";

        //创建线程
        /* 参数一:线程对象 传递线程对象的地址
           参数二:线程属性 包括线程的优先级等
           参数三:子线程需要执行的方法
           参数四:需要传递的参数
         */
        int result = pthread_create(&thread, NULL, func, (__bridge void *)(str));
        if (result == 0) {
            NSLog(@"线程创建成功了^_^");
        } else {
            NSLog(@"创建线程失败 ^o^。创建结果是:%d", result);
        }
        //手动把当前线程结束掉
        // pthread_detach:设置子线程的状态设置为detached,则该线程运行结束后会自动释放所有资源。
        pthread_detach(thread);

//下面是传递的执行方法
void * func(void *param)
{
        //打印当前线程
        NSLog(@"当前线程是:%@",  [NSThread currentThread]);
    return NULL;
}

3.2、NSThread

NSThread 是OC 面向对象,简单易用,只负责创建,不用管理线程死亡,偶尔使用
NSThread是一个类,有三种初始化方法:

//创建线程
   NSThread * newThread = [[NSThread alloc]initWithTarget:self selector:@selector(demo:) object:@"Thread"];
   //或者
   NSThread  * newThread =[[NSThread alloc]init];
   NSThread  * newThread = [[NSThread alloc]initWithBlock:^{
       NSLog(@"通过block创建");
   }];

开启线程:

[newThread start];

暂停线程:

[NSThread sleepForTimeInterval:1.0]; (一秒为例)
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];

线程取消:

[newThread cancel];

停止线程:

[NSThread exit];

设置优先级:
iOS8之前:

[NSThread currentThread];

iOS8之后:

//通过qualityOfService枚举的方式设置优先级
NSQualityOfServiceUserInteractive:最高优先级,用于用户交互事件

NSQualityOfServiceUserInitiated:次高优先级,用于用户需要马上执行的事件

NSQualityOfServiceDefault:默认优先级,主线程和没有设置优先级的线程都默认为这个优先级

NSQualityOfServiceUtility:普通优先级,用于普通任务

NSQualityOfServiceBackground:最低优先级,用于不重要的任务


比如设置最低优先级
[newThread setQualityOfService: NSQualityOfServiceBackground];

设置线程名称:

[newThread setName:@"自定义的线程名"];

获取主线程

[NSThread mainThread];

检查是否为主线程:

[thread isMainThread];

线程之间通讯

//指定当前线程操作
[self performSelector:@selector(func)];
[self performSelector:@selector(func) withObject:nil];
[self performSelector:@selector(func) withObject:nil afterDelay:2.0];

//子线程回到主线程操作,比如在主线程刷新UI
[self performSelectorOnMainThread:@selector(func) withObject:nil 
waitUntilDone:YES];

//主线程指定其他线程执行操作
[self performSelector:@selector(func) onThread:newThread 
withObject:nil waitUntilDone:YES]; 
//这里指定为后台线程
[self performSelectorInBackground:@selector(func) withObject:nil];

线程同步:
在程序运行中,因为资源在内存中是共享的,如果存在多条线程,那么各个线程读写资源就会存在先后顺序或者同时读写,因此会出现读写混乱或者错误。为了防止这样的事情发生我们要在读写数据时加锁,这样在操作同一个数据时候线程只有一个,一个操作完成后另一个才能操作。但上锁就会加大CPU开销,造成性能降低。
iOS中NSLock / NSConditionLock / NSRecursiveLock / @synchronized都可以实现线程上锁的操作。具体用法很简单,随便搜索都一大把结果这里就不一一说明了。

3.3、 GCD

GCD(Grand Central Dispatch)是iOS4引入的强大的线程处理技术,它是基于XNU内核开发的,性能极为优越。
这里有我以前写的一篇GCD应用可以移步去看一下详细应用。

3.4、NSOperation

NSOperation 是对GCD的封装,NSOperation是一种抽象类,完全面向对象化,使用的时候要使用其子类调取方法或者继承它自定义具体的操作。相比于GCD简单易用,代码可读性高。
NSOperation有两个子类NSBlockOperation和NSInvocationOperation,使用区别是一个是block回调,一个是NSInvocation回调

3.4.1、 NSInvocationOperation子类

//创建操作对象,封装执行的任务
NSInvocationOperation *operation=[[NSInvocationOperation alloc]initWithTarget:self selector:@selector(test) object:nil];
     
    //执行操作
    [operation start];

执行操作就会调用target的test方法,不过操作对象时候默认在主线程中执行,只有添加到队列中才会开启新的线程。默认情况下如果没放到队列Queue中都是同步执行的,只有将NSInvocationOperation 对象放到NSOperationQueue中才会开启线程异步执行。

3.4.2、NSBlockOperation子类

//创建NSBlockOperation操作对象
     NSBlockOperation *operation=[NSBlockOperation blockOperationWithBlock:^{
          //执行的操作1
      }];
      
     //和NSInvocationOperation不同的是NSBlockOperation有一个添加操作的方法
     [operation addExecutionBlock:^{
          //执行的操作2
      }];
     [operation addExecutionBlock:^{
          //执行的操作3
      }];
     [operation addExecutionBlock:^{
          //执行的操作4
      }];
//开启执行操作    
     [operation start];

需要注意的是只要NSBlockOperation封装的操作数大于1,就会进行异步执行操作
3.4.3、NSOperationQueue
NSOperation可以通过start方法来执行任务,但默认是同步执行,除非NSBlockOperation封装的操作数大于1的时候才会异步执行。但是如果把NSOperation对象添加到NSOperationQueue(队列)中,系统会自动异步执行NSOperation中的操作任务,添加操作任务到NSOperationQueue中自动开启线程执行任务。

    //创建NSOperationQueue
      NSOperationQueue * queue=[[NSOperationQueue alloc]init];
      //把操作添加到队列中
     //第一种方式
      [queue addOperation:operation1];
      [queue addOperation:operation2];
      [queue addOperation:operation3];
      //第二种方式
      [queue addOperationWithBlock:^{
         //直接添加操作
     }];
    // 第三种直接添加NSOperation数组,不过如果Bool类型的wait为yes的话会阻塞当运行前线程直到队列全部添加完成。如果为no就相当于批量添加到队列中。
      [queue addOperatios:(NSArray<NSOperation *> *)ops  waitUntilFinished:(BOOL)wait];

相关文章

网友评论

      本文标题:iOS开发 多线程的运用

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