目录
- 概要
- NSThread
- GCD
- NSOperation
- 多线程与锁
1.概要
- 进程
进程指系统中独立正在运行的程序,每个进程都运行在其专用且受保护的内存空间中。
- 线程
线程是进程的基本执行但也,一个进程(程序)的所有任务都在线程中执行,每个进程至少要有一个线程。
1个线程中任务的执行是串行的。
- 多线程
1个进程可以开启多条线程,每条线程可以并行(同时)执行不同的任务。
- 多线程的原理
同一时间,CPU只能处理一条线程,只有1条线程工作(执行)。
>多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)。
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。
- 多线程优缺点
优点
-
[ ] 能适当提高程序的执行效率。
-
[ ] 能适当提高资源利用率(CPU、内存利用率)
缺点
-
[ ] 开启线程需要占用一定的内存空间(默认情况下,主线城占用1M,子线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能。
-
[ ] 线程越多,CPU在调度线程上的开销就越大。
-
[ ] 会使得程序开发变得复杂。
-
主线程
一个iOS程序运行后,默认开启一条线程,成为‘主线程’或‘UI线程’,主要负责显示/刷新UI界面,处理UI事件。注意:别将比较耗时的操作放到主线程中
多线程实现方式
- pthread
-
[ ] 简介:
1.一套通用的多线程API 2.适用Unix\Linux\Windows等系统 3.跨平台\可移植 4.使用难度大
C语言,线程生命周期需要程序员管理,几乎不用
- NSThread
- [ ] 简介:
1.使用更加面向对象
2.简单易用,可直接操作线程对象
OC,线程生命周期需要程序员管理,偶尔使用
- GCD(Grand Central Dispatch)
- [ ] 简介:
1.用来替代NSThread等技术
2.充分利用设备的多核
C语言,线程生命周期自动管理,经常使用
- NSOperation
-
[ ] 简介:
1.基于GCD(底层是GCD) 2.比GCD多了一些更简单实用的功能 3.使用更加面向对象
OC语言,线程生命周期自动管理,经常使用
NSOperation是个抽象类,并不具备封装操作的能力,必须使用他的子类。
使用NSOperation子类的方式有3种
- [ ] NSInvocationOperation
- [ ] NSBLockOperation
- [ ] 自定义子类继承NSOperation,实现内部相应的方法。
2.NSThread
- [ ] 创建、启动线程
//方式1:创建线程后手动启动
NSThread *thread = [[NSthread alloc] initWithTarget:self selectot:@selectot(run) object:nil];
[thread start];
//方式2:创建线程后自动启动
[NSthread detachNewThreaSelectot:@selectot(run) toTarget:self withObject:nil];
//方法3:隐式创建线程并自动启动
[self performSelectInBackground:@selector(run) withObject:nil];
- [ ] 相关方法
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; //是否为主线程
+ (BOOL)isMainThread; //是否为主线程
+ (NSThread *)currentThread; // 获得当前线程
//线程的调度优先级
+ (double)threadPriorty;
+ (BOOL)setThreadPriorty:(double)p;
-(double)threadPriorty;
- (BOOL)setThreadPriorty:(double)p;
//调度优先级的取值为0.0~1.0,默认为0.5,值越大,优先级越高。
//线程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
- [ ] 控制线程状态
//启动线程
- (void)start;
//进入就绪状态->运行状态。当线程任务执行完毕,自动进入死亡状态。
//阻塞(暂停)线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
//进入阻塞状态
//强制停止线程
+ (void)exit;
//进入死亡状态
**注意** 一旦线程停止(死亡)了,就不能再次开启任务
3. GCD(Grand Central Dispatch)
队列的执行顺序FIFO(先进先出)
- [ ] 同步和异步
- 同步(sync): 只能在当前线程中执行任务,不具备开启新线程的能力
- 异步(async): 可以在新的线程中执行任务,具备开启新线程的能力
- [ ] 并发队列和串行队列
- 并发队列: 可以让多个任务并发执行,并发功能只有在异步函数下才有效
- 串行队列: 让任务一个接着一个地执行
注意
同步和异步主要决定能不能开启新线程
并发和串行决定执行方式
几种组合情况
- async + 并发队列(最常用)
会创建线程,同时开启多条,任务并发执行
- async + 串行队列(有时候用)
会开一条线程,串行执行
- sync + 串行队列
不会开启线程,串行执行
- sync + 并发队列
不会开启线程,串行执行
- async + 主队列(添加到主队列中的任务,都会自动放到主线程中执行)
主要用于线程通信
- sync + 主队列(不能用)
主队列中串行执行,同步函数互相等待,死锁
4. NSOperation
使用NSOperation子类的方式有3种
- [ ] NSInvocationOperation
- [ ] NSBLockOperation
- [ ] 自定义子类继承NSOperation,实现内部相应的方法。
NSOperationQueue的一些高级用法
1.设置最大并发数为2条
queue.maxConcurrentOperationCount = 2;
2.线程依赖(不能互相依赖)
【operationB addDependency:operationA];
【operationC addDependency:operationB];
3.取消队列的所有操作
- (void)cancelAllOperations;
//也可以调用NSOperation的 - (void)cancel 方法取消单个操作
4.暂停和恢复队列
- (void) setSuspended:(BOOL)b;//YES表示暂停队列,NO代表恢复队列
5.主队列
[NSOperationQueue mainQueue];
//添加到主队类的操作会在主线程运行,放在其他队列的操作在子线程运行
6.线程通信
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//回到主线程需要的操作
}];
自定义NSOperation
自定义NSOperation的步骤很简单
重写- (void)main方法,在里面实现想执行的任务
重写- (void)main方法的注意点
自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
4.多线程与锁
多线程中的隐患
- 资源抢夺:一块资源可能被多个线程共享,会产生资源竞争,造成数据混乱。
解决方案:加锁(后续补充)
- [x] 互斥锁:
使用格式:
@synchronized(锁对象){
//需要锁定的代码
}
**注意:**锁定1份代码只用一把锁,用多把锁是无效的
互斥锁的优缺点:
优点: 能有效防止因多线程抢夺资源造成的数据安全问题
缺点: 需要销毁大量的CUP资源
互斥锁的使用前提: 多条线程抢夺同一块资源
线程之间的通信
// 回到主线程,刷新UI界面(为了线程安全)
1.[self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO];
2.[self performSelector:@selector(downloadFinished:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
或者直接用对象调用 [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
注意: waitUntilDone后面的参数表示是否等待线程通讯完毕
//GCD回到主线程
disptach_async(dispatch_get_main_queue(),^{
//回到主线程需要的操作
})
//NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
//回到主线程需要的操作
}];
延时
// 延迟执行不要用sleep,坏处:卡住当前线程
1. [NSThread sleepForTimeInterval:3];
// 一旦定制好延迟任务后,不会卡主当前线程
2. [self performSelector:@selector(download:) withObject:@"http://555.jpg" afterDelay:3];
// 3秒后自动开启新线程 执行block中的代码
3. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
NSLog(@"------task------%@", [NSThread currentThread]);
});
网友评论