美文网首页
iOS多线程基础

iOS多线程基础

作者: 星辰流转轮回 | 来源:发表于2018-06-02 15:10 被阅读20次

什么是进程?

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

什么是线程?

一个进程想要执行任务,必须得有线程.(每一个进程至少要有一个线程)

一个进程中的所有任务都在线程中执行

线程的串行

一个线程中的所有任务的执行顺序都是串行的,也就是说,在同一时间内,一个线程只能执行一个任务.只有执行完上一个任务才可以执行下一个;也可以任务,线程是进程中的一条执行路径

什么是多线程?

一个进程中可以开启多条线程,每条线程可以并行执行不同的任务

多线程技术可以提高程序的执行效率

多线程原理

同一时间,1核CPU只能处理一条线程,只有一条线程在工作;

多线程并发(同时)执行,其实就是CPU快速的在多线程之间调度(切换);

如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象;

如果线程开的非常多

CPU会在N条线程之间调度,CPU会累死,消耗大量CPU资源;

每条线程被调度执行的频次降低,线程的执行效率会降低

多线程的优缺点

优点:能适当提高程序的执行效率;能适当提高资源利用率  (CPU资源利用及内存利用)

缺点:创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB),栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize设置,但必须是4K的倍数,而且最小必须是16K), 创建线程大约需要90毫秒的时间;如果开启大量的线程,就会降低程序的性能;线程越多,CPU在调度线程上的开销就越大;程序设计更加复杂,比如线程之间的通信,多线程的数据共享

什么是主线程?

一个iOS程序运行后,默认会开启一条线程,称为”主线程”或”UI线程”

主线程的主要作用:显示\刷新界面;处理UI事件,如触摸,点击,拖拽,滑动;

使用注意:耗时操作不要放在主线程,耗时操作会卡住主线程,严重影响UI的流畅度,会给用户造成App假死现象

苹果开发,约定刷新UI的操作需要放在主线程! 其实刷新UI的操作放到子线程不一定会崩溃,但是往往会发生一些意想不到且很难找出原因的问题!


iOS多线程的实现方案 有4中方式:

 phtread :C语言开发,线程周期需要程序员管理,因为调用的是函数,实际开发一般用不到;

NSThread:OC语言开发,使用可直接操作线程对象,线程周期需要程序员管理,实际开发使用较少;

GCD:C语言开发,自动管理线程周期,开发中经常使用;

NSOperation:OC语言开发,基于GCD(其实NSOperation比GCD出来早,后来苹果貌似做了底层重写),使用更加面向对象,开发中经常使用



NSThread 使用

一个NSThread对象就代表一条线程

创建一个NSThread

- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullableid)argument; 

 返回一个NSThread对象,可以对线程进行管理操作

selector: 调用的方法

argument:给调用方法传递的参数

- (void)start; //启动线程 线程一启动,就会在线程thread中执行self的selector方法

主线程相关用法

+ (NSThread*)mainThread;//获得主线程

- (BOOL)isMainThread;//是否为主线程

+ (BOOL)isMainThread;//是否为主线程

[NSThread currentThread]; //获得当前线程

thread.name=@"customThread"; //给线程设置名称,程序崩溃的时候,能够获取都程序崩溃所在线程

+ (void)detachNewThreadWithBlock:(void (^)(void))block;

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

从线程中分离出一条新的线程来执行

- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullableid)arg;

InBackground 在后台运行,即子线程运行;是NSObject的分类, 意味着所有继承于NSObject的对象都可以使用这个方法

上述2种创建线程方式的优缺点

优点:简单快捷

缺点:无法对线程进行更详细的设置

控制线程状态

- (void)start;//启用线程  进入就绪状态->运行状态.当线程任务执行完毕,自动进入死亡状态

+ (void)sleepUntilDate:(NSDate*)date;

+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

//阻塞线程 进入阻塞状态

+ (void)exit;

//强制停止线程, 会使线程进入死亡状态  注意:一旦线程停止(死亡),就不能再次开启任务

多线程的使用,有资源共享的安全隐患

多个线程可能会访问同一块资源

比如多个线程访问同一个对象、同一个变量、同一个文件

当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题

解决方案:

1.互斥锁  --保证锁内的代码,同一时间,只有一条线程运行

@synchronized(锁对象) {//需要锁定的代码 }

互斥锁的范围,应该尽量小,如果大了,效率就会差

参数可以是任意的OC对象,一般用 self 全局对象

局部变量,是每一个线程单独拥有的,因此没法加锁

优点:能有效防止因多线程抢夺资源造成的数据安全问题

缺点:需要消耗大量的CPU资源

2.原子属性 --atomic

原子属性原理:先把文件保存在一个临时的文件中,等全部写入之后,再改名

原子属性的目的:多个线程写入这个对象的时候,保证同一时间只有一个线程能够执行!

单写多读的一种多线程技术,同样有可能出现"脏数据",重新读一下.

实际上,原子属性内部有一个锁,自旋锁

 自旋锁 & 互斥锁

 - 共同点:

    都能够保证线程安全.

 - 不同点:

    互斥锁:如果线程被锁在外面,哥么就会进入休眠状态,等待锁打开,然后被唤醒!

    自旋锁:如果线程被锁在外面,哥么就会用死循环的方式,一直等待锁打开!

 无论什么锁,都很消耗新能.效率不高

3.线程同步

多条线程在同一条线上执行(按顺序地执行任务)

线程间通讯

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullableid)arg waitUntilDone:(BOOL)wait;

耗时操作执行完毕,需要回主线程更新UI 就用到了线程间通讯 

wait:让当前线程等待 (如果当前线程是主线程!设置YES是没有用的)


相关文章

网友评论

      本文标题:iOS多线程基础

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