多线程第一天
多线程的作用:耗时操作不卡主屏幕
//任务执行的两种方式: 同步/异步
1->同步有顺序.异步没有先后顺序
异步执行 无法控制速度.和进程
多线程就是研究异步执行的
开线程:performSelectoerInBackGround
进程/线程
进程的作用:维持程序的存在
进程:在系统中
正在运行的一个应用程序
每个进程之间是独立的.每个进程有一个独立的保护的内存空间
线程:
一个进程至少要有一个线程
线程是进程的基本执行单元.一个进程的所有任务都在线程中执行
多线程:一个进程可以开启多个线程 ,多个可以”同时”执行不同的任务
多线程可以解决程序阻塞的问题
多程序可以解决程序的执行效率的问题
多线程的执行原理
“多线程的同时执行”,是cpu快速的在多个线程之间的切换
cpu在多个线程之间做切换操作
多线程的优缺点
优点:能适当的提高程序的执行效率
能适当的提高资源的利用率(cpu 内存)
线程上的任务执行完成后 线程会自动销毁
缺点:
1⃣️开启线程需要占用一定的内存空间(开辟512kb)
2⃣️如过开启大量的线程 会真用大量的内存空间 减低程序的性能
3⃣️线程越多.cpu在调用线程上的开销就越大 1->cup开销2->内存 3->时间
线程保存在栈 区 (Stack space)
Creation time 90毫秒
4⃣️程序设计更加复杂, 比如多线程的数据共享.线程间的通信(聊天)
主线程
一个程序运行后,默认会开启一个线程,成为主线程或者”ui线程”
程序一启动主线程 操作都是ui操作
主线程一般用来刷仙UI界面 处理是事件
不要将耗时操作放到主线程
实现异步执行:开线程
iOS中多线程的指数方案
1⃣️ POSIX可移植的线程的操作系统接口(Protable Operating System InterFace )
pthread :多线程的API 跨平台/可移植
2⃣️NSthread :面向对象
3⃣️GCD 替代NSthread 充分利用设备的多核
语言C
4⃣️NSOperation:基于GCD
pthread:
1.导入头文件
2phtread_creat(___) 里面都是传地址
参数一 :子线程的编号/代号/ID/传入地址
参数二:子线程的属性.此处不知设置线程属性 传入NULL 空指针 nil是空对象
参数三:指定子线程执行的函数,
void * (*) (void *)
返回值 函数名 函数的参数
c语言的三大要数:返回值,函数名.函数的参数
void* :标识可以纸箱任何地址的指针,类似于oc中的id(万能指针可以指向任何对象的地址)
函数的地址: 就是函数名
prthread_t 数据类型 ID
返回值是int 如果成功 创建子程序成功返回0;如果失败返回非零—>成功的原因只有一个
/**
* pthrad 演练 , 创建子线程
*/
- ( void )pthreadDemo
{
NSLog ( @"pthreadDemo %@" ,[ NSThread currentThread ]);
/*
参数 1 : 子线程的编号 / 代号 /ID. 传入地址
参数 2 : 子线程的属性 . 此处不设置线程属性 . 传入 NULL. 空指针 .nil 是空对象
参数 3 : 指定子线程执行的函数
void * (*) (void *)
返回值 函数名 函数的参数
void * : 表示可以指向任何地址的指针 . 类似于 OC 中的 id( 万能指针 , 可以指向任何对象的地址 )
函数的地址 : 就是函数名 .
参数 4 : 传入指定子线程执行的函数参数中的数据
返回值 : int, 如果创建子线程成功 , 返回 0; 如果失败返回非零 . 因为成功的结果只有一个 , 失败的原因有很多 .
<NSThread: 0x7fcd1b507490>{number = 1, name = main}
number 等于 1 的时候 , 是主线程
<NSThread: 0x7fcd1ca041b0>{number = 2, name = (null)}
number 不等于 1 的时候 , 是子线程
提示 : 不要纠结 number 到底等于几 , 因为是 CPU 分配的 , 只是线程创建的编号的累加
*/
// 参数 1
pthread_t ID;
int result = pthread_create (&ID, NULL , demo , NULL );
if (result == 0 ) {
NSLog ( @" 成功 " );
} else {
NSLog ( @" 失败 " );
}
}
// 指定子线程执行的函数
void *demo( void *param)
{
NSLog ( @"demo %@" ,[ NSThread currentThread ]);
return NULL ;
* pthrad 演练 , 创建子线程
*/
- ( void )pthreadDemo
{
NSLog ( @"pthreadDemo %@" ,[ NSThread currentThread ]);
/*
参数 1 : 子线程的编号 / 代号 /ID. 传入地址
参数 2 : 子线程的属性 . 此处不设置线程属性 . 传入 NULL. 空指针 .nil 是空对象
参数 3 : 指定子线程执行的函数
void * (*) (void *)
返回值 函数名 函数的参数
void * : 表示可以指向任何地址的指针 . 类似于 OC 中的 id( 万能指针 , 可以指向任何对象的地址 )
函数的地址 : 就是函数名 .
参数 4 : 传入指定子线程执行的函数参数中的数据
返回值 : int, 如果创建子线程成功 , 返回 0; 如果失败返回非零 . 因为成功的结果只有一个 , 失败的原因有很多 .
<NSThread: 0x7fcd1b507490>{number = 1, name = main}
number 等于 1 的时候 , 是主线程
<NSThread: 0x7fcd1ca041b0>{number = 2, name = (null)}
number 不等于 1 的时候 , 是子线程
提示 : 不要纠结 number 到底等于几 , 因为是 CPU 分配的 , 只是线程创建的编号的累加
*/
// 参数 1
pthread_t ID;
int result = pthread_create (&ID, NULL , demo , NULL );
if (result == 0 ) {
NSLog ( @" 成功 " );
} else {
NSLog ( @" 失败 " );
}
}
// 指定子线程执行的函数
void *demo( void *param)
{
NSLog ( @"demo %@" ,[ NSThread currentThread ]);
return NULL ;
}
__bridge桥接
在MRC环境下.谁申请,谁释放
在Arc环境下,编译器在编根据译的时候,会根据代码的结构,在合适的位置添加release,retain,sutolerease语句
在ARC环境下,编译器 不会管理C语言申请的内存空间
桥接目的:告诉编译器,什么都不做
在mrc环境下需要使用桥接么?不需要,因为该环境下,内存是手动管理的
NStread创建线程的三种方式
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self threadDmoe3];
}
// 隐式创建
- (void)threadDmoe3
{
// @interface NSObject (NSThreadPerformAdditions)
// 方便凡是继承自NSObject的对象,都可以很方便的调用线程相关的方法
[self performSelectorInBackground:@selector(demo:) withObject:@"perform"];
}
// 类方法
- (void)threadDmoe2
{
// detach : 分离
// 无法拿到线程对象,无法手动开启子线程
// 自动启动子线程
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"detach"];
}
// 对象方法
- (void)threadDmoe1
{
// Target : 哪个对象
// selector : 哪个对象的哪个方法
// 执行哪个对象的哪个方法
// 创建线程对象
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"alloc"];
// 启动子线程
[thread start];
}
- (void)demo:(id)param
{
NSLog(@"%@ %@",param,[NSThread currentThread]);
{
[self threadDmoe3];
}
// 隐式创建
- (void)threadDmoe3
{
// @interface NSObject (NSThreadPerformAdditions)
// 方便凡是继承自NSObject的对象,都可以很方便的调用线程相关的方法
[self performSelectorInBackground:@selector(demo:) withObject:@"perform"];
}
// 类方法
- (void)threadDmoe2
{
// detach : 分离
// 无法拿到线程对象,无法手动开启子线程
// 自动启动子线程
[NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"detach"];
}
// 对象方法
- (void)threadDmoe1
{
// Target : 哪个对象
// selector : 哪个对象的哪个方法
// 执行哪个对象的哪个方法
// 创建线程对象
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"alloc"];
// 启动子线程
[thread start];
}
- (void)demo:(id)param
{
NSLog(@"%@ %@",param,[NSThread currentThread]);
}
线程的状态 线程的生命周期
1⃣️ alloc init :内存中出现一个线程对象 :状态 :新建
2⃣️ start方法 出现一个可调度的线程池 线程对象进入线程的可调度线程池
nsthread 只能做到这一步,后面的调用由cpu负责调用 也就是只能到达新建状态
一下是CUP操作的
3⃣️ Runable 就绪状态
cou调度当前线程 运行状态 .当CPU调用其他线程,则又进入就绪状态,
4⃣️ 如果调用了sleep方法/等待同步锁 该线程会从可调度线程池中移除,但没有销毁,进入内存中,进入阻塞状态;
��️ sleep到时,得到同步锁 该线程回到可调度线程池, 结束阻塞状态进入就绪状态,
6⃣️结束完毕,销毁,正常死亡
7⃣️非正常死亡
线程的状态
06线程的属性
线程的执行是由CPU决定的,我们控制不了
name:标识线程 追踪线程 追踪bug
threadPriority 优先级 范围是0.0-1.0 默认是0.5
不能决定线程之间的先后顺序,只是线程��更多的机会呗CPu执行
开发建议,千万
不要设置threadPriority
打印内存大小 子线程的内存空间的大小 单位kb
NSLog(@”%tu”,[NSthread currentThread].stackSize);
主线程和子线程都是512kb
07
多线程操作共享资源的问题(缺点)
共享资源:一个对象 一个变量 一个文件
共享资源:全局的对象,文件,可以被其他对象访问的资源
当多个线程错做同一块资源时 很容易引发数据错乱和数据安全
数据错乱理论依据:多个线程之间的切换
@synchronized(self)
互斥锁,同步锁 利用了线程的同步技术
互斥锁和同步锁的特点 : 能够保证被锁定的代码同一时间只能由一个线程访问或者访问,间接的实现了一个单线程.
锁定的范围是共享的资源读写部分,而且锁定的范围必须小
self :锁对象之一 ,任何继承自noObject的对象都是锁对象,内部都有一把锁,默认是开启的
锁对象必须是全局的对象,不能是局部对象.
局部的锁对象 每次线程执行都是一个新的对象 锁不住属性.self是最方便获取的全局锁对象
8原子属性 线程”安全的"
nonatomic
atomic 原子属性.默认的
保证同一时间只有一个线程能够写入,但是同一时间多个线程都可以取值
单写多读. 单个线程写入.多核线程可以读取; 所以不是绝对的安全
atomic 本身就有一把锁(自旋锁)写在set方法里面 我们看不到 自旋锁看不到.
nonatomic和atomic对比
atomic 线程安全 需要消耗大量的资源
nonatomic 非线程的.适合内存次小的移动设置
ios开发建议:声明为nonatomic
模拟原子属性 重写set,get方法,我们重写setget方法,没有_属性,所以要用合成指令@synthesize obj=_obj;
互斥锁:如果发现其他线程正在执行锁定代码,线程会进入休眠(就绪状态) 等其他线程时间到开锁后,线程会被唤醒(执行)
自旋锁:如果发现其他线程正在锁定代码.线程会以死循环的方式一直等待
线程安全:
多个线程同时操作共享资源是不安全的,多个线程同时操作一个全局变量
线程安全:在多个线程进行读写操作时,仍然能够保证数据的正确
主线程(UI线程):
几乎所有UIKI类库的类都是线程不安全的,所以更新UI的操作都在主线程 主线程唯一,没有多线程,防止同时操作.为了不出错,所以控件要求都在主线程上面执行
所有包含NSMUtable的类都是线程不安全的
为什么UIKIT不加锁,,保证流畅
查看4,40需求分析
一旦重写loadView 控制器就不会从SB中加载当self.view==nil的时候就会调用loadView
线程间的通信 当一个线程中的数据传递到其他线程,
当创建对象 不希望立即销毁, 用strong 直接引用计数加1,不让他销毁;
控件strong和weak的 strong只会出现重复强引用,不影响
用weak只是保存,
strong强引用 引用计数加1;
拖控件连线的时候为什么用weak: 拖到sb的时候subViews已经对控件有一个强引用
自动释放池
消息循环 类似于一个死循环.消息是一个动作,捕捉消息的死循环
作用:1保证程序不退出
2.捕捉交互,传递响应 检测事件的
内存泄露 该释放的没有释放
内存溢出 内存满了
野指针 :不该释放的释放了