二多线程深入理解
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二多线程深入理解相关的知识,希望对你有一定的参考价值。
1、线程间资源共享/抢夺
(1)定义:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,当多个线程访问同一块资源时,各个线程提取和修改数据不同步,很容易引发数据错乱和数据安全问题。
(2)互斥锁(线程同步) :解决上面的问题
- · 代码:@synchronized(锁对象) { // 需要锁定的代码 }
- · 每一个对象(NSObject)内部都有一个锁(变量),当有线程要进入synchronized到代码块中会先检查对象的锁是打开还是关闭状态,默认锁是打开状态(1),如果是线程执行到代码块内部 会先上锁(0)。如果锁被关闭,再有线程要执行代码块就先等待,直到锁打开才可以进入。
- · 互斥锁的实现流程
线程执行到synchronized
i. 检查锁状态 如果是开锁状态转到ii ,如果上锁转到v
ii. 上锁(0)
iii. 执行代码块
iv. 执行完毕 开锁(1)
v. 线程等待(就绪状态)
2、原子属性
(1)属性中的修饰符
- nonatomic :非原子属性
- atomic : 原子属性,针对多线程设计的,默认值。保证同一时间只有一个线程能够写入,但是同一个时间多个线程都可以取值。
- (2)自旋锁:atomic 本身就有一把锁(自旋锁),保证“单写多读”:单个线程写入,多个线程可以读取。如果发现有其它线程正在锁定代码,线程会用死循环的方式,一直等待锁定的代码执行完成 。自旋锁更适合执行不耗时的代码。
- (3)ios开发的建议
- 所有属性都声明为nonatomic,移动设备内存小,atomic虽然相对线程安全,但是消耗资源较多。
- 尽量避免多线程抢夺同一块资源。
- 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力。
3、线程安全
- (1)多个线程同时操作一个全局变量是不安全的,使用自旋锁并不是绝对的安全(因为单写多读)。
- (2)线程安全:在多个线程进行读写操作时,仍然能够保证数据的正确 。使用互斥锁可以实现,但是消耗性能。
- (3)关于主线程(UI线程):几乎所有UIKit??提供的类都是线程不安全的,所有更新UI的操作都在主线程上执行。
4、NSRunLoop
(1)功能作用:运行循环,又叫消息循环或事件循环。
- 检测、接受“输入事件”并执行。
- 保证程序不退出(主线程)。
- 如果没有事件发生,会让程序进入休眠状态
(2)特点:
- NSRunLoop不能单独存在,必须存在于线程中,不论主线程中还是子线程中都有一个消息循环。注意:主线程的RunLoop是默认开启的,子线程中的RunLoop默认不开启。
- 只要线程一启动,内部就会有一个默认的主RunLoop,而每一个App,只要一启动,就会自动有一个主线程。
(3)(两大核心之一)输入事件:输入源(比如键盘输入,滚动scrollView,performSelector方法等等),定时源(定时器)
(4)(两大核心之二)运行模式(消息循环模式):
- 线程的消息循环运行在某一种消息循环模式上。
- 输入事件必须设置消息循环的运行模式,并且如果想让输入事件可以在消息循环上执行,输入事件的消息循环运行模式必须和当前消息循环的运行模式一致
- 两种常用的运行模式:NSDefaultRunLoopMode、NSRunLoopCommonModes。
- 例子:输入事件是定时源
//定时源 (计时器)
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(demo) userInfo:nil repeats:YES];
/*
参数1:输入源
参数2:输入源的模式,要和当前消息循环的模式对应,才可以让消息循环执行输入源
NSDefaultRunLoopMode默认模式 NSRunLoopCommonModes包含了很多种模式
*/
[[NSRunLoop currentRunLoop]addTimer:timer forMode:NSRunLoopCommonModes];
5、自动释放池
- (1)主线程自动释放池的创建和销毁:
- 每一次主线程的消息循环开始的时候会先创建自动释放池。
- 消息循环结束前,会释放自动释放池。
- 自动释放池被销毁或耗尽时会向池中所有对象发送 release 消息,释放所有 autorelease 的对象。
- 自动释放池随着消息循环的开始和结束不断的重建和销毁。
(2)子线程的自动释放池:
- 在子线程开启时手动创建释放池。因为主线程可以自动生成释放池,而子线程不可以。为了保证消息循环结束(线程结束)时,所有的对象可以正常入池和释放,必须手动添加。
- · 其他情况和主线程相同。
- (3)什么时候使用自动释放池:(官方文档建议)
- · 开启子线程时。
例如:
for (int i = 0; i < largeNumber; ++i) {
@autoreleasepool {
NSString *str = @"Hello World";
str = [str stringByAppendingFormat:@" - %d", i];
str = [str uppercaseString];
}
}
(4)示意图
以上是关于二多线程深入理解的主要内容,如果未能解决你的问题,请参考以下文章