什么是进程
> 进程是指在系统中正在运行的一个应用程序
> 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内
什么是线程
> 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)
> 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行
什么是多线程
> 1个进程中可以开启多条线程,每条线程可以并发(同时)执行不同的任务
> 进程 à 车间,线程 à 车间工人
> 多线程技术可以提高程序的执行效率
多线程的原理啊
> 同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
> 多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
> 如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
多线程优缺点
优点:
> 能适当提高程序的执行效率
> 能适当提高资源利用率(CPU、内存利用率)
缺点:
> 开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
> 线程越多,CPU在调度线程上的开销就越大
> 程序设计更加复杂:比如线程之间的通信、多线程的数据共享
关于主线程(iOS)
什么是主线程
> 一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
主线程的作用
> 显示\刷新UI界面
> 处理UI事件(比如点击事件、滚动事件、拖拽事件等)
主线程的使用注意
> 别将比较耗时的操作放到主线程中
> 耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验
iOS开发中,用于多线程开发技术,主要由以下几种
技术方案 |
简介 |
语言 |
生命周期 |
使用频率 |
Pthread |
ü 一套通用的多线程API ü 适用于Unix\linux\window等系统 ü 可跨平台,可移植 ü 适用难度大 |
C |
程序员管理 |
几乎不用 |
NSThread |
ü 使用更加面向对象 ü 简单易用,可直接操作线程对象 |
OC |
程序员管理 |
偶尔使用 |
GCD |
ü 用于替代NSThread等线程技术 ü 充分利用设备的多核 |
C |
自动管理 |
经常适用 |
NSOperation |
ü 基于GCD(底层是GCD) ü 比GCD多一些更简单使用的功能 ü 适用更加面向对象 |
OC |
自动管理 |
经常适用 |
关于pthread
由于pthread几乎不用,所我将用几个简单的案例说明,直接上代码
1 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 2 3 [self began]; 4 } 5 6 7 8 // iOS开发,一般C语言的框架.h文件没有注释 9 // http://baike.baidu.com 10 11 // 使用pthread创建线程 12 - (void)began 13 { 14 // 声明一个线程变量 15 pthread_t threadId; 16 17 //传给在线程中运行的函数的参数 18 id str = @"hello"; 19 20 21 pthread_create(&threadId, NULL, run, (__bridge void *)(str)); 22 } 23 // 线程中运行的耗时函数 24 void *run(void *param) 25 { 26 NSString *str = (__bridge NSString *)(param);//接收穿件来的函数 27 28 // 耗时操作放在这里执行 29 for (int i = 0; i < 20000; i++) { 30 NSLog(@"%@----%@", [NSThread currentThread], str); 31 } 32 //[NSThread currentThread]:打印当前线程 33 34 return NULL; 35 }
四个参数的解释
1. 要开的线程的变量
2. 线程的属性
3. 要在这个子线程执行的函数(任务)
4. 这个函数(任务)需要传递的参数
关于NSThread
我也讲通过代码来介绍NSThread
首先是创建NSThread的三种方式
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self began1]; } //创建线程方法1 - (void)begin1 { //实例化一个线程对象,这个方法有利于线程属性的应用 NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"hellow"]; //此方法需要手动发个线程开始工作,启动线程,在新开的线程中执行run方法 [thread start]; } //创建线程方法2 - (void)begin2 { [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"函数参数"]; } //创建线程方法3 - (void)begin3 { [self performSelectorInBackground:@selector(run:) withObject:@"函数参数"]; } // 耗时操作,子线程中进行的函数 - (void)run:(NSString *)str { for (int i = 0; i < 10; i++) { NSLog(@"%@--%d", [NSThread currentThread], i); } }
NSThread的属性
- (void)test4 { NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"hello"]; //线程名字,在[NSTread currentThread]的打印中可以看到 threadA.name = @"thraed A"; // 线程优先级 // 是一个浮点数,0.0~1.0。 默认值 0.5 // 开发的时候,一般不去修改优先级的值。 // 优先级,必须调用很多次的时候,才能体现出来。 threadA.threadPriority = 0.1; // 开始工作 [threadA start]; NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"hello"]; threadB.name = @"thraed B"; // 线程优先级 // 是一个浮点数,0.0~1.0。 默认值 0.5 threadB.threadPriority = 1.0; // 开始工作 [threadB start]; }
控制线程状态
1.启动线程
-(void)start;
2.阻塞线程
+(void)sleepUntilDate:(NSDate *)date;
+(void)sleepForTimeInterval:(NSTimeInterval)ti;
3.强制停止线程
+(void)exit;
注意:程序一旦进入停止(死亡)状态,就不能再启动任务.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self began]; } - (void)began { // 1. 新建一个线程 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; // 2. 放到可调度线程池,等待被调度。 这时候是就绪状态 [thread start]; } - (void)run { //进来就阻塞(休息)几秒 [NSThread sleepForTimeInterval:5.0]; //指定休息到什么时候 [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:5.0]]; //休息玩进入耗时操作 for (int i = 0; i < 20; i++) { //也可再满足某一条件后进入休息 if (i == 10) { [NSThread sleepForTimeInterval:2.0]; } //也可让他在某一条件下终止线程的执行 if (i == 15) { [NSThread exit]; } NSLog(@"%@---%d",[NSThread currentThread], i); } NSLog(@"线程结束"); }
多线程的安全隐患
1.一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
2.当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题
可通过互斥锁来解决安全隐患问题
互斥锁的使用格式
@synchronized(锁对象,一般为self){//需要锁住的代码块}
注意:锁定一份代码只能用一把锁,用多把锁是无效的
互斥锁的有缺点
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源
但是苹果公司不推荐使用互斥锁,因为性能太差了.
所以,苹果公司默认将所有程序的更新UI都在主线程进行,也就不会出现多个线程改变一个资源
而在主线程更新UI的好处有"
1.只在主线程更新UI,就不会出现多个线程同时改变一个UI控件
2.主线程的优先级最高,也就意味着UI的更新优先级高,会让用户感觉很流畅,提升客户体验;\.
关于原子atomic和非原子nonatomic属性
atomic:原子属性,为setter方法加锁(默认就是atomic),线程安全的,需要消耗大量的资源
nonatomic:非原子属性,不会为setter方法加锁,非线程安全,适合内存小的移动设备,iOS开发中基本用该属性.