首先,对于ios的线程应用,我们先说说他的一些概念:
线程
是用来执行任务的,线程彻底执行完任务A才能去执行任务B。为了同时执行两个任务,产生了多线程。
我打开一个视频软件,我开辟一个线程A让它执行下载任务,我开辟一个线程B,用来播放视频。我开辟两个线程后,这两个任务能同时执行,如果我开辟一个线程,只有下载任务完成,才能去执行播放任务。
线程相当于店里的服务员,一个服务员同时只能做一件事情,多顾几个服务员,就能同时做很多事情。
What(理解多线程之前要先了解进程和线程)
进程
进程是应用程序的执行实例,简单来说就是在操作系统中运行的程序。例如我在手机上只打开QQ和微信这两个软件,系统中就会有两个进程存在。
进程不能执行任务
进程在运行时创建的资源随着进程的终止而死亡。
线程
进程本身是不能执行任务的,进程想要执行任务必须的有线程,线程是进程内部的一个独立的执行单元,同时只能执行一个任务,相当于一个子程序。线程被分为两种,主线程(用户界面线程)和子线程(工作线程或称为后台线程)。我在望京(操作系统)开了一个橘子产品体验店(进程),里面有很多工作人员,有店长帮我布置门面(主线程),咨询人员(子线程)、销售人员(子线程)。
线程执行完毕就会被销毁。
主线程(也称父线程):当应用程序启动时自动创建和启动,通常用来处理用户的输入并响应各种事件和消息。主线程的终止也意味着该程序的结束。
子线程:由主线程来创建,用来帮助主线程执行程序的后台处理任务。如果子线程A中又创建一个子线程B,在创建之后,这两者就是相互独立的,多个子线程之间效果上可以同时执行。
一个进程中可以有多个线程,并且所有线程都在该进程的虚拟地址空间中,可以使用进程的全局变量和系统资源。
线程状态:线程的五种状态
多线程
目前大多数的app,都需要连接服务器,而访问服务器的速度可能快也可能很慢。如果一个app访问服务器的操作没有在子线程操作的话,在该app访问服务器的过程中,该软件是不能响应用户的操作的,只有该app访问结束以后,app才能响应用户的操作,这就造成线程阻塞,也就是我们常见的卡顿现象。一条线程在同一时间内只能执行一个任务,但是进程可以有多条线程。可以开启多条线程来执行不同的任务,从而提高程序的执行效率,避免线程阻塞。
操作系统会根据线程的优先级(线程的优先级可以手动设置)来安排CPU的时间,优先级高的线程,优先调用的几率会更大,同级的话,看线程执行的先后。
同一时间内,CPU只能处理一条线程,只有一条线程在工作。多线程并行执行,其实就是各个线程不断切换,因为执行切换的时间很快很快,就造成了同时执行的假象,原理如下,比如A,B两个线程;
A执行到某一时间段要切换了,可A任务没完成,系统就会把A当前执行的位置和数据以入栈的方式保存起来
然后B线程执行,B执行时间到了,它的位置状态等也会被系统保存到B的栈中。
系统自动找到A的栈,将A之前保存的数据恢复,又可以从A之前断开的状态继续执行下去,如此循环
系统每开一个线程都有比较大的开销。若线程开的过多,不仅会占用大量内存和让程序变得更加复杂,而且会加重CPU的负担,这样的软件,会使你的手机在冬天变成暖手宝。
Why(为什么使用多线程)
提高程序执行效率,避免线程阻塞造成的卡顿现象。
能适当提高资源利用率(CPU,内存)。
不可滥用多线程:
开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB,可以自己设置内存大小,但必须是4的倍数),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
线程越多,CPU在调度线程上的开销就越大
程序设计更加复杂:比如线程之间的通信、多线程的数据共享
总结:
线程与进程的关系
线程是CPU执行任务的基本单位,一个进程可以有多个线程,但同时只能执行一个任务。
进程就是运行中的软件,是动态的。
一个操作系统可以对应多个进程,一个进程可以有多条线程,但至少有一个线程
同一个进程内的线程共享进程里的资源
主线程
进程一启动就自动创建
显示和刷新UI界面
处理UI事件
子线程的作用
处理耗时的操作
子线程不能用来刷新UI
说完了理论,再来实际运用一下:
1、创建一个UIImageView,并放在父视图上
imageView = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 200, 200)];
[self.view addSubview:imageView];
2、创建一个子线程
<span style="background-color: rgb(153, 255, 255);"> //自动开启子线程的方法
[NSThread detachNewThreadSelector:@selector(thread1:) toTarget:self withObject:@"thread1:"];
/* // 手动创建子线程的方法
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(thread:) object:@"thread"];
[thread start];//开启线程
*/</span>
3、通过url 获取网络图片
//从网络加载图片 并将它转化为data类型的数据
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrl]];
image = [UIImage imageWithData:data];//kUrl是图片地址宏定义,此处为了方便使用
4、回到主线程
//添加 刷新UI是主线程的范畴 需要转到主线程执行 waitUntilDone 设为YES 意味着UI更新完才会去做其他操作
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];
提示:3、4都需要在子线程方法thread:中执行.
5、在主线程更新
imageView.image =kimage;//在updateUI:中给imageView提供图片就可以显示了
如果加载多张图片的情况下:
//通过线程休眠实现 实现图片的顺序加载
[NSThread sleepForTimeInterval:[index intValue]]; //index为传进来的方法的图片顺序,这样时间
越来越长 就可以实现图片的顺序加载
//如果你点击的时候 线程没有执行完 取消线程(点击屏幕,取消未完成的线程)
if (thread.isFinished == NO) {
[thread cancel];
}
//这个在获得开始点击状态的方法里面实现
//退出线程
NSThread *thread = threadArray[[index intValue]];
if (thread.isCancelled == YES) {
[NSThread exit];
}//退出线程的时候要在睡眠之后,因为线程要在你设定的睡眠时间之后才会运行,但要在主线程之前,将已经运行
完的UI数据刷新出来