OC底层原理18-线程编程

iOS--OC底层原理文章汇总

线程

    1. 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行,一个进程可以有多个线程。
    1. 进程想要执行任务,必须得有线程,进程至少需要一条线程。
    1. 程序启动默认开启一条线程,这条线程被称为主线程或UI线程。

进程

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

譬如Mac可以通过“活动监视器”查看系统中所开启的进程。

线程与进程的关系

  • 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间
  • 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、CPU等,但是进程之间资源是相互独立
  1. 一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程奔溃会导致整个进程都死掉。所以多进程要比多线程健壮。
  2. 进程切换时,效率高,但消耗的资源大。所以涉及到频繁切换时,使用线程要比进程好。同样如果要同时进行并且又要共享某些变量的并发操作,只能用线程而不能用进程。
  3. 执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立运行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  4. 线程是处理器调度的基本单位,但进程不是。
  5. 线程没有地址空间,线程包含在进程的地址空间中。

安卓开发可以有多个进程;iOS往往是单进程。

多线程

多个线程在多CPU下时,效率是非常之高的,相比于一个CPU在一个进程中只能执行一条线程,现如今的多核CPU对多线程并发处理逻辑提供非常好的支持。但多线程也会有其优点和不足

优点

    1. 可以提高应用程序的感知响应能力和在多核系统上的实时性能。
    1. 能适当提高资源的利用率(CPU、内存)。
    1. 线程上的任务执行完成后,线程会自动销毁。

缺点

  • 1.开启线程需要占用一定的内存空间(默认情况下,每一个线程都占512KB)。
    1. 如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
    1. 线程越多,CPU在调度线程上的开销就越大。
    1. 程序设计会变得更加复杂,比如线程间的通信、多线程的数据共享。

多线程 & CPU

  • 单核CPU在同一时间,CPU只能处理1个线程,即此时只有1个线程在执行。
  • 多线程同时执行: 指CPU快速的在多个线程之间切换;CPU调度线程的时间足够块,就造成了多线程“同时”执行的效果。
  • 如果线程足够多,CPU会在N个线程之间切换,消耗大量的CPU资源;每个线程被调度的次数越低,线程的执行效率越低。

多线程的技术方案

在iOS中使用的多线程分为以下四种:pthread、NSThread、GCD、NSOperation,下图为各自的区别

四大多线程方案

内存区

进程、线程的执行都是需要依托内存去执行的,同一进程内的线程会共享本进程的内存。而「内存」会有多种,在iOS中分为:栈区、堆区、全局区(静态区)、常量区、代码区。接下来看看各自内存五大区都负责什么。

栈区(stack)

  • 在程序创建临时变量时(即在运行时),由系统自动分配,当不需要时自动清除的变量的存储区。
  • 变量:局部变量、函数参数等。
  • 在一个进程中,编译器用来实现函数调用的地方是用户栈,它位于虚拟地址空间的顶部,栈地址是连续存储且向下扩展,遵循先进后出原则(FILO);用户栈在程序执行期间可以动态的扩展和收缩,这个是栈和堆的共同点。
  • 栈区大小根据系统不一。在iOS主线程下,大概空间为1M;辅助线程为512KB;MacOS主线程下,大概空间为8M。

按照苹果文档介绍,辅助线程允许的最小堆栈大小为16 KB,并且堆栈大小必须为4 KB的倍数。在线程创建时会在进程空间中预留此内存的空间,但是直到需要它们时,才会创建与该内存关联的实际页面。这还取决于CPU负载,计算机速度以及可用系统和程序内存的数量。

内存创建空间需求

堆区(Heap)

  • 由类创建对象而开辟的内存空间,譬如:alloc 、new等。
  • 它是不连续的存储空间,地址是由低向高扩展,遵循先进先出原则(FIFO),这与栈相反。
  • 堆亦可以动态的扩展与收缩。这表现在运行时的对象的创建与释放。现如今开发都是在ARC环境下,对象的释放由系统操作完成,当该对象的应用计数为0时,就会被release掉。MRC下则需要程序员手动释放。

通过下面的例子可以看看栈区地址和堆区地址的不同

// 定义一个Acount类
@interface Account()

@end
@implementation Account
- (void)printWithName:(NSString *)name
{
   // 参数name是一个指针,指向传入的参数指针所指向的对象内存地址。name是在栈中
  NSLog(@"name指针地址:%p,name指针指向的对象内存地址:%p",&name,name);
}

  /* account 是指针变量,在栈中;[Account alloc]开辟的内存空间就是在堆中
  *  account 指针指向了[[Account alloc]init]所创建的对象。
  */
  Account *account = [[Account alloc]init];

通过打印地址可以知道,传入参数的对象地址与print方法参数的对象指针地址不一样,但是内存地址是一样的,p account 打印的则是堆空间地址,一般以0x6开头,栈空间地址一般以0x7开头。

全局区(静态区)

  • 全局变量和静态变量存储的区域。程序运行即一直存在,程序结束后由系统释放。
    (全局变量是指变量值可以在运行时被动态修改,而静态变量是static修饰的变量,包含静态局部变量和静态全局变量。)
  • 未初始化全局区:.bss段
  • 初始化全局区:.data段
静态变量有两种
  • 全局静态变量

优点:不管对象方法还是类方法都可以访问和修改全局静态变量,并且外部类无法调用静态变量,定义后只会指向固定的指针地址,供所有对象使用,节省空间。

缺点:存在的生命周期长,从定义直到程序结束。

建议:从内存优化和程序编译的角度来说,尽量少用全局静态变量,因为存在的声明周期长,一直占用空间。程序运行时会单独加载一次全局静态变量,过多的全局静态变量会造成程序启动慢。

  • 局部静态变量

优点:定义后只会存在一份值,每次调用都是使用的同一个对象内存地址的值,并没有重新创建,节省空间,只能在该局部代码块中使用。

缺点:存在的生命周期长,从定义直到程序结束,只能在该局部代码块中使用。

建议:局部和全局静态变量从根本意义上没有什么区别,只是作用域不同。如果值仅是一个类中的对象和类方法使用并且值可变,可以定义全局静态变量,如果是多个类使用并可变,建议值定义在model作为成员变量使用。如果是不可变值,建议使用宏定义 ,譬如:static NSString * value;

常量区(cosnt)

  • 存放常量且不允许修改的存储区,在编译时已经确定,程序结束后由系统释放。一般用于接口或者文字显示这种固定值。添加extern可以对外全局常量,任意位置都可以访问。
// .h中定义extern
extern NSString *const name;
// .m中定义值
NSString *const name = @"123";

代码区

  • 编译时分配的主要用于存放程序运行时的代码。代码会被编译成二进制文件存进内存。

内存五大区示意图


内存五大区

线程生命周期

线程的生命周期可以用下面的一个示意图来概括

线程生命周期
  1. 当一个线程对象被创建,调用start方法后会处于就绪状态,此时线程加入可调度线程池等待执行;CPU通过调度当前线程运行它,当线程运行完之后,结束线程;CPU在调度线程过程中,如果CPU负载小,CPU的多核机制会调用其他线程同时执行。
  2. 当一个线程正在运行过程中,如果调用了Sleep or 同步锁阻塞线程执行,会将运行线程重可调度池中移出;当Sleep阻塞结束,会重新将线程加入可调度池进入就绪状态。

线程池执行策略

线程池执行策略

线程安全

在一个进程中,多个线程在同时执行,线程之间可能会访问同一地址空间资源,这样就会导致数据错乱,这个时候线程安全就变得尤为重要。加锁就是很好的解决方案。

锁是用于线程编程中保持线程同步的方式或排除并发的一种策略。为了防止多线程访问资源的抢夺。锁可以轻松保护大部分代码,保证锁内的代码,同⼀时间,只有⼀条线程能够执⾏,从而可以确保该代码的正确性。

关于锁的使用及介绍,后续文章再详细分析。

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