iOS开发 多线程的运用

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

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

总纲

1.概念相关

2.应用场景

3.相关技术和用法

1.概念相关

1.1、什么是多线程?

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

1.2、线程与进程的区别?

程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序过程就称之为进程。进程是程序执行的一次活动。进程中有线程在执行,且一个进程中最少有一条线程,这条线程我们叫它为主线程(主线程中运行着runloop),主线程一般显示、刷新UI界面,处理UI事件(点击、滚动、拖拽事件等),我们也一般叫它UI线程。当然进程也也可以开多条线程,我们称之为子线程??嗵跸叱痰氖褂们榭鑫颐浅莆嘞叱獭?/p>

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];
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352

推荐阅读更多精彩内容

  • 目录: (一)线程与进程之间的区别 (二)为什么需要学习多线程 (三)多线程任务执行方式 (四)多线程执行的...
    KingLionsFrank阅读 778评论 6 6
  • 在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案...
    张战威ican阅读 603评论 0 0
  • 主队列 细心的同学就会发现,每套多线程方案都会有一个主线程(当然啦,说的是iOS中,像 pthread 这种多系统...
    京北磊哥阅读 375评论 0 1
  • Pa_1130阅读 136评论 0 0
  • 一自挥泪别故乡, 听尽东西南北腔。 人生世间一过客, 青山白骨自平常。
    想念山东的你阅读 390评论 3 8