iOS底层系列22 -- 多线程基础概念

进程

  • 进程是指在系统中正在运行的一个应用程序;
  • 每个进程之间是相互独立的,每个进程均运行在其专用的且受保护的内存空间内;

线程

  • 线程是进程的基本执行单元,一个进程的所有任务都是在线程中执行的;
  • 进程要想执行任务,必须的有线程,一个进程进程至少要有一条线程;
  • APP应用程序启动会默认开启一条线程,这条线程被称为 主线程 或者 UI线程;

进程与线程之间的关系

  • 进程之间的地址空间是相互独立,不能交叉访问,同一个进程内的线程共享本进程的地址空间;
  • 进程之间的资源是相互独立的,同一个进程内的线程共享本进程的资源;
  • 两个之间的关系就相当于工厂与流水线的关系,工厂与工厂之间是相互独立的,而工厂中的流水线是共享工厂的资源的,即进程相当于一个工厂,线程相当于工厂中的一条流水线;

线程与RunLoop之间的关系

  • RunLoop与线程是一一对应的,其保存在一个全局的字典当中;
  • RunLoop是来管理线程的,当线程的runloop被开启后,线程会在执行完任务后进入休 眠状态,有了任务才会被唤醒去执行任务;
  • 对于主线程来说,RunLoop在程序一启动就默认创建好了,在线程结束时被销毁;
  • 对于子线程来说,RunLoop不会默认创建, 其在第一次获取时被创建,所以在子线程用定时器要注意:确保子线程的RunLoop被创建,不然定时器不会回调;

多线程

  • iOS中的多线程同时执行的本质是: CPU在多个任务之间进行快速的切换,由于CPU调度线程的时间足够快,就造成了多线程的“同时”执行的假象,让我们认为多个线程在同时执行任务,其中切换的时间间隔就是时间片

多线程的优缺点

优点:
  • 能适当提高应用程序的执行效率;
  • 能适当提高系统资源的利用率,如CPU、内存;
  • 线程上的任务执行完成后,线程会自动销毁;
缺点:
  • 开启线程需要占用一定的内存空间,默认情况下,每一个线程占用512KB,如果开启大量线程,会占用大量的内存空间,降低程序的性能;
  • 线程越多,CPU在调用线程上的开销就越大;
  • 程序设计更加复杂,比如线程间的通信,多线程的数据共享;

多线程的生命周期

线程的生命周期通常分为5个部分:创建 -- 就绪 -- 运行 -- 阻塞 -- 死亡;其状态之间的切换如下图所示:

Snip20210319_208.png
  • 创建:即实例化线程对象;
  • 就绪:线程对象调用start方法,将线程对象加入可调度线程池,等待CPU的调用,即调用start方法,并不会立即执行,而是进入到就绪状态,需要等待一段时间,经CPU调度后才执行;
  • 运行:CPU负责从可调度线程池中调度线程然后执行,在线程执行完成之前,其状态可能会在就绪和运行之间来回切换,这个变化是由CPU负责,开发人员不能干预;
  • 阻塞:当满足某个预定条件时,可以让线程休眠,即sleep,或者使用同步锁,阻塞线程执行,会将线程从可调度线程池中移除,当线程解除sleep时/获取到锁,会重新将线程加入到可调度线程池中。下面关于休眠的时间设置,都是NSThread的;
  • 死亡:分为两种情况
    • 正常死亡,即线程执行完毕;
    • 非正常死亡,即当满足某个条件后,在线程内部(或者主线程中)终止执行(调用exit方法等退出)
  • 简要说明,就是处于运行中的线程拥有一段可以执行的时间(称为时间片),如果时间片用尽,线程就会进入就绪状态队列,如果时间片没有用尽,且需要开始等待某事件,就会进入阻塞状态队列;等待事件发生后,线程又会重新进入就绪状态队列;
  • 每当一个线程离开运行,即执行完毕或者强制退出后,会重新从就绪状态队列中选择一个线程继续执行;
  • 线程的exit和cancel说明
    • exit:一旦强行终止线程,后续的所有代码都不会执行;
    • cancel:取消当前线程,但是不能取消正在执行的线程;

线程池的工作原理

  • 其工作原理如下图所示:
Snip20210319_210.png
  • 首先线程池有两个重要参数分别为:corePoolSize和maximumPoolSize;
  • corePoolSize表示核心线程池能创建核心线程的最大数量;
  • maximumPoolSize表示线程池能创建线程的最大数量;核心线程池包含在线程池中。
  • 【第一步】:当有任务提交过来,首先判断核心线程池是否已满(corePoolSize)
    • 未满,创建核心线程执行任务;
    • 已满,进入第二步;
  • 【第二步】:判断工作队列是否已满
    • 未满,将任务添加到工作队列中;
    • 已满,进入第三步;
  • 【第三步】:判断线程池是否已满(maximumPoolSize)
    • 未满:创建非核心线程执行任务;
    • 已满:进入第四步;
  • 【第四步】:执行饱和策略,
  • 通常有以下四种饱和策略:
    • AbortPolicy(抛出一个异常,默认的)
    • DiscardPolicy(新提交的任务直接被抛弃)
    • DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
    • CallerRunsPolicy(交给线程池调用所在的线程进行处理,即将某些任务回退到调用者)

iOS中多线程的实现方案

  • 主要有四种分别为:pthreadNSThreadGCDNSOperation
Snip20210319_211.png
  • 下面通过代码案例分别演示这四种多线程方案的实现:
【pthread】
//线程回调函数
void * pthreadTest(){
    NSLog(@"===>%@", [NSThread currentThread]);
    return NULL;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    pthread_t threadId = NULL;
    //c字符串
    char *cString = "HelloCode";
    //创建一个字线程
    int result = pthread_create(&threadId,NULL,pthreadTest,cString);
    if (result == 0) {
        NSLog(@"pthread 创建成功");
    }else{
        NSLog(@"pthread 创建失败");
    }
}
  • 需导入#import <pthread.h>
Snip20210319_212.png
【NSThread】
Snip20210319_214.png
【GCD】
Snip20210319_215.png
【NSOperation】
Snip20210319_217.png

线程安全问题

  • 同一时刻多条子线程共同访问共享资源数据,容易引发数据错乱和数据安全问题,有以下两种解决方案:
    • 互斥锁(即同步锁):@synchronized
    • 自旋锁;
互斥锁
  • 用于保护临界区,确保同一时间,只有一条线程能够访问执行;
  • 加了互斥锁的代码,当新线程访问时,如果发现有其他线程正在访问共享资源,新线程就会进入休眠状态;
  • 互斥锁的锁定范围,应该尽量小,锁定范围越大,效率越差;
  • 能够加锁的任意的NSObject对象;
  • 锁对象一定要保证所有的线程都能够访问;
自旋锁
  • 自旋锁与互斥锁类似,但它不是通过休眠使线程阻塞,而是在获取锁之前一直处于忙等(即原地打转,称为自旋)阻塞状态;
  • 锁持有的时间短,且线程不希望在重新调度上花太多成本时,就需要使用自旋锁,属性修饰符atomic,本身就有一把自旋锁;
  • 加入了自旋锁,当新线程访问代码时,如果发现有其他线程正在锁定代码,新线程会用死循环的方法,一直等待锁定的代码执行完成,即不停的尝试执行代码,比较消耗性能;
【面试题】:自旋锁 vs 互斥锁
  • 相同点:在同一时间,保证了只有一条线程访问共享资源;
  • 不同点:
    • 互斥锁:发现其他线程执行,当前线程 休眠(即就绪状态),进入等待执行,即挂起,一直等其他线程打开之后,然后唤醒执行;
    • 自旋锁:发现其他线程执行,当前线程一直询问(即一直访问),处于忙等状态,耗费的性能比较高;
  • 场景:根据任务复杂度区分,使用不同的锁,但判断不全时,更多是使用互斥锁去处理
    当前的任务状态比较短小精悍时,用自旋锁,反之用互斥锁;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • iOS 底层探索: 学习大纲 OC篇[/p/9d73ee7aae64] 前言 这篇开始探索多线程的底层原理,这篇主...
    欧德尔丶胡阅读 256评论 0 2
  • 多线程,一直是解决内存暴增方法的重点,图片、视频、大量数据的的下载现在总结一下 基本概念 进程:应用程序的执行实例...
    艳晓阅读 274评论 0 0
  • 在开始之前,先理解以下几点一、进程和线程的区别和联系:1、地址资源:进程有自己的内存地址,进程内的线程可以共享进程...
    淡定的笨鸟阅读 1,303评论 0 5
  • 本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗...
    水中的蓝天阅读 102评论 0 2
  • 推荐指数: 6.0 书籍主旨关键词:特权、焦点、注意力、语言联想、情景联想 观点: 1.统计学现在叫数据分析,社会...
    Jenaral阅读 5,701评论 0 5