多线程技术
process进程:在系统中运行的一个应用程序;每个进程都是独立的;有专有的内存空间
thread线程:程序的一段执行序列,进程的不部分;
特点:1、进程只分配内存空间,并不执行任务
2、每个进程至少有一个线程,该线程叫做主线程
3、线程是进程的基本执行单元,进程的所有任务都是在线程中执行
4、每个线程中得任务的执行都是串行的
多线程并发:一个进程中多条线程并发执行任务;
特点: 1、提高程序的执行效率,提高资源利用率
2、同一时间,CPU只能处理一条线程
3、多线程并发时,CPU在多条线程间快速切换
4、线程切换的 速度很快,就造成了多线程的并发执行
5、开启线程需要内存空间,线程太多会造成调度消耗太多资源
6、线程过多会降低每条线程被调度的频率(线程执行效率降低)
多线程的应用:
主线程:显示刷新UI界面、处理UI事件;耗时任务(如下载)放在子线程中
判定方法在哪个线程中执行:NSLog(@“当前的线程:”,[NSThread currentThread]);
四种多线程技术:pthread NSThread GCD NSOperation
1.pthread:
基于c语言的API ,可编写多平台应用,使用难度大,需要手动创建销毁线程,可自定义功能少
pthread_t pthread;
void *task(void *data){
NSLog(@“当前子线程:%@“,NSThread currentThread];
return 0;
}
pthread_create(&pthread,NULL,task,NULL);
2.NSThread:
- 基于OC,相对于pthread使用简单,面向对象
- 缺点:需要自己管理线程的生命周期,线程同步,加锁、开锁,管理多个线程比较困难
手动创建线程的三种方法:
1、NSThread实例方法:- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument NS_AVAILABLE(10_5, 2_0);(只创建)
2、NSThread类方法:+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; (创建并运行)
3、NSThread实例方法:- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);
注:selector: 线程执行的方法,这个selector只能由一个参数,而且没有返回值
object: 传给target的唯一参数,也可以为nil
target: selector消息发送的对象 只有第一个能设置名字、优先级
线程阻塞(延迟):
- // sleep⽅法⼀:开始睡眠5秒钟
[NSThread sleepForTimeInterval:5];
- // sleep⽅法二:
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:3
[NSThread sleepUntilDate:date];
强制停止线程:
[NSThread exit];
其他常用方法:
- +(NSThread *)mainThread; //获得主线程
- -(BOOL)isMainThread;//判断是否是主线程
- +(double)threadPriority;//获取线程优先级 优先级取值范围0.0~1.0 值越大优先级越高
- +(BOOL)setThreadPriority:(double)p;//设置优先级
- -(NSString*)name;//获取线程名字
- -(void)setName:(NSString*)n;//设置线程名字
线程的状态:
- New(新建) -> Runnable(就绪) --> cpu开始调度当前线程 ---> Running(运行) ---> 调用 sleep方法 ---> Blocked(阻塞) ---> 移除线程池 ---> sleep执行完毕/到时 ---> 进入线程 池 ---> Runnable(就绪)
- 如果线程任务执行完毕/异常/强制退出 ---> Dead(死亡)
加锁:
- 尽量避免使用@synchronized,也就是避免多个线程访问同一个资源,因为有了加锁、 解锁需要消耗比较大的cpu资源
- 加锁的前提:多个线程同时访问同一个资源的时候才需要加锁(互斥锁)
- 线程同步:多条线程在同一条线上执行(按顺序地执行任务)
- 尽量讲加锁、资源抢夺的业务逻辑交割服务器端处理,减少移动客户端的压力
synchronized关键字
1、synchronized关键字的作用域有二种:
1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){},它的作用域是当前对象;
原子和非原子属性:
- nonatomic: 非原子 —> 不会在set方法中加锁,这个是推荐方式,会占用资源少
- atomic:原子 —> 在set方法中加锁,防止多个线程同时执行set方法
线程间的通讯:
1.在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信
2.线程间通讯的方式:
- 一个线程传递数据给另一个线程
- 在一个线程中执行完特定任务后,转到另一个线程继续执行任务
子线程返回主线程的两种方法:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait
***********************************************************************************************
3. GCD
并行队列只有在执行多个异步任务的时候体现出来并发现象,因为在线程快速切换时体现出并行;
同步执行的任务 会致使后面的任务提交阻塞;
GCD:Grand Central Dispath 大中央调度
- GCD的基本思想就是将操作(任务)放在队列中去执行
- 队列负责调度任务执行所在的线程以及具体的执行时间
- 队列的特点是先进先出,新添加至队列的操作(任务)都会排在队尾
- 串行队列中的任务会按顺序执行
- 并行队列中的任务通常会并发执行,而且无法确定任务的执行顺序
- 在串行队列中执行同步任务:不会新建线程,按顺序执行任务(毫无用处)
- 在串行队列中执行异步任务,会新建线程,按顺序执行任务(非常有用)
- 在并行队列中执行同步任务:不会新建线程,按顺序执行任务(几乎没用)
- 在并行队列中执行异步任务:会新建多个线程,但是无法确定任务的执行顺序(有用,但是很容易出错)
- 全局队列是系统的,直接拿过来就可以用,与并行队列类似,但是不能指定队列的名字,调试时无法确认任务所在队列
- 在全局队列中执行同步任务:不会新建线程,按顺序执行任务
- 在全局队列中执行异步任务:会新建多个线程,但是无法确定任务的执行顺序
- 如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程。
- 每一个应用程序只有一个主线程即只有一个主队列
- 为什么需要再主线程上执行任务呢?因为在ios开发中,所有UI的更新任务都必须在主线程上执行。
- 主队列中的操作都是在主线程中执行的,不存在异步的概念
- 在主线程中向 主队列中添加的同步操作会死锁
线程阻塞(延迟):三种方法
1 //过3秒后做一件事儿,这个做法会导致主线程阻塞,不提倡使用 2 - (IBAction)sleepDelay:(id)sender 3 { 4 NSLog(@"3秒后干件事儿!"); 5 [NSThread sleepForTimeInterval:3]; 6 NSLog(@"干事儿中..."); 7 } 8 - (IBAction)performAfterDelay:(id)sender 9 { 10 NSLog(@"3秒后干件事儿!"); 11 //在3秒后会启动一个线程去完成任务(调用方法task),此方法并不会阻塞 12 [self performSelector:@selector(task) withObject:nil afterDelay:3]; 13 NSLog(@"主线程继续向下执行..."); 14 } 15 - (void)task 16 { 17 NSLog(@"干活中..."); 18 } 19 //使用GCD实现延迟执行 20 - (IBAction)dispatchAfter:(id)sender 21 { 22 /* 23 1秒 = 1000毫秒 24 1毫秒 = 1000微秒 25 1微秒 = 1000纳秒 26 */ 27 //dispatch_time函数的第二个参数单位是纳秒 28 NSLog(@"3秒钟后做事"); 29 dispatch_after( 30 /** 31 * 延迟的函数 32 * 33 * @param DISPATCH_TIME_NOW 从现在起 34 * @param int64_t 延迟描述 35 * NSEC_PER_SEC 这个宏的意思是每秒多少纳秒 36 * @return 无 37 */ 38 dispatch_time(DISPATCH_TIME_NOW, (int64_t)3 * NSEC_PER_SEC), 39 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), 40 ^{ 41 NSLog(@"3秒后我开始做需要的事情啦!"); 42 }); 43 }
线程间的通讯:
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执⾏耗时的异步操作... dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程,执⾏UI刷新操作 }); });
dispatch_once 参照:线程安全的单例 http://www.cnblogs.com/ChrisYu/p/4651114.html
小结:
无论什么队列和什么任务,线程的创建和回收不需要程序员参与,由队列来负责,程序员只需要面对队列和任务。GCD在后端管理这一个线程池,GCD不仅决定着Block代码块将在哪个线程中被执行,而且还可以根据可用的系统资源对这些线程进行管理,从而让开发者从线程管理的工作中解放出来,通过GCD这种集中的管理线程,缓解了大量的线程被创建的问题
4. NSOperation
单词:
1. global ge(0) lao(1) bao(3) 全局
2. concurrent 并发
3. queue 队列
作业:
1. 从网上下载图片并显示到界面上。
要求,下载过程中界面不能死。
用GCD做
2. 了解一个第三方框架SDWebImage
1 - (IBAction)serialOperation 2 { 3 //默认OperationQueue中的线程都是并行的 4 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 5 NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil]; 6 // [operation1 start];//不会启动线程,直接在主线程中执行 7 [queue addOperation:operation1]; 8 NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil]; 9 //[operation2 start]; 10 //线程二要依赖线程一,所以线程2会等线程一结束了再执行,按此方式可以让两个线程串行执行 11 [operation2 addDependency:operation1]; 12 [queue addOperation:operation2]; 13 } 14 15 - (void)printPlusSignal 16 { 17 for (int i=0; i<10; i++) { 18 [NSThread sleepForTimeInterval:1]; 19 NSLog(@"++++++++++++++"); 20 } 21 } 22 23 - (void)printMinusSignal 24 { 25 for (int i=0; i<10; i++) { 26 [NSThread sleepForTimeInterval:1]; 27 NSLog(@"----------------"); 28 } 29 } 30 31 - (IBAction)concurrentQueue:(id)sender 32 { 33 //默认OperationQueue中的线程都是并行的 34 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 35 NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printPlusSignal) object:nil]; 36 37 [queue addOperation:operation1]; 38 NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(printMinusSignal) object:nil]; 39 40 [queue addOperation:operation2]; 41 } 42 43 //使用Block提交任务给OperationQueue 44 - (IBAction)blockQueue 45 { 46 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 47 //CallBack 回调 48 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ 49 for (int i=0; i<10; i++) { 50 [NSThread sleepForTimeInterval:1]; 51 NSLog(@"++++++++++++++"); 52 } 53 }]; 54 [queue addOperation:operation1]; 55 56 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ 57 for (int i=0; i<10; i++) { 58 [NSThread sleepForTimeInterval:1]; 59 NSLog(@"-------------"); 60 } 61 }]; 62 [queue addOperation:operation2]; 63 } 64 - (IBAction)mainQueue 65 { 66 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 67 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 68 NSLog(@"在子线程中-----%@", [NSThread currentThread]); 69 //获取主队列(回到主线程) 70 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; 71 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ 72 NSLog(@"在主线程中?-----%@", [NSThread currentThread]); 73 }]; 74 [mainQueue addOperation:operation2]; 75 }]; 76 [queue addOperation:operation]; 77 }