iOS面试

iOS之多线程(待续)

2017-10-26  本文已影响19人  SK丿希望

什么是多线程?

说白了就是CPU快速的在多条线程之间调度(即切换),且多条线程可以并发执行(即同时执行)
提到多线程,不得不提一下进程

那什么是进程了?

可以理解为在系统中正在运行的程序,每个进程之间是独立的,而且每个进程均运行在其专有且受保护的内存空间.

什么线程?

独立执行的代码段,一个线程同时间只能执行一个任务,反之多线程并发就可以在同一时间执行多个任务。

那进程和线程有什么关系了?

其实一个进程想执行任务,那么他就需要线程,因为进程中的所有任务都是在线程中执行的,故每个进程至少拥有一条线程(即我们的主线程)

在说下主线程吧?

又称UI线程,其实就是一个iOS程序运行后,默认开启的第一条线程,我们称为主线程,作用主要是
1.处理UI事件(比如点击事件,滚动事件,拖拽事件等等),
2.刷新UI

那再来说说多线程的意义吧?(个人理解)

举个例子,就像我们听歌一样,如果我在同时听歌的时候又想下载歌曲,如果只有一条线程的话,我们知道一条线程同一时间只能执行一个任务,为了满足这个需求,多线程的意义在此,所谓的多线程主要是就是CPU快速在多条线程之间调度,(并发执行)

那么多线程有哪些实现方案了?

1.pthread (C语言 是一套通用的多线程API 线程的生命周期是需要程序员手动管理的)
2.NSThread (OC 使用更加面向对象 可以直接操作线程对象 但是线程的生命周期也是需要程序员手动管理的)

3.GCD (C语言 充分利用设备的多核 生命周期自动管理)

4.NSOperation (OC 基于GCD的 使用更加面向对象 线程生命周期自动管理)

多线程的优缺点?

有利必有弊嘛没有十全十美的,当然多线程也不例外

优点:

1.能够适当的提高程序的执行效率
2.适当的提高资源利用率(CPU,内存利用率)

缺点:

1.如果大量的开启线程,会降低程序的性能
2.线程越多,那么CPU的工作量越大
3.程序的设计就更加复杂(例如线程之间的通信,多线程的数据共享)(推荐3到5条)

NSThread(个人用的比较少)

一个NSThread对象就代表一条线程,生命周期需要手动管理


Snip20171026_1.png

线程状态有

1.新建New (进入就绪状态->运行状态.当线程任务执行完毕,自动进入死亡状态-> - (void)start)
2.就绪Runnable
3.强制停止线程
4.运行状态 Running
5.阻塞 Blocked
6.死亡 (一旦线程停止(死亡)了,就不能再次开启任务了)


Snip20171026_2.png

互斥锁

1.使用格式:@synchronized(锁对象) // 需要锁定的代码
2.使用优缺点:
a.优点:能防止因为多线程抢夺资源造成的数据安全问题.
b.缺点:需要消耗大量的CPU资源
3.使用原理:就是使用了线程同步技术,多条线程在同一条上执行(按顺序的执行任务)


NSOperation

首先NSOperation是一个抽象类,并不具备封装操作的能力,必须使用NSOperation子类的方式有3种:
NSInvocationOperation
NSBlockOperation
自定义子类继承NSOperation,实现内部相应的方法


Paste_Image.png
实现步骤

1.先将要执行的操作封装到一个NSOperation对象中
2.然后将NSOpration对象添加到NSOprationQueue(队列)中
3.系统会自动将NSOprationQueue中的NSOpration取出来
4.将取出来的NSOpration封装的操作放到一条新线程中执行

那NSOprationQueue又是什么了?

我们称之为队列,只要是自己创建的队列,就会在子线程中执行,而且默认是并发的,且队列可以取消,暂停,恢复,但是这些操作只会对后面未执行的任务进行操作,不会影响当前正在执行的,且取消不可恢复
自己创建: alloc/init --> 默认是并发 --> 也可以让它串行
主队列 : mainQueue

Paste_Image.png
NSOperationQueue的作用

NSOperation可以调用start方法来执行任务,但默认是同步执行的
如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
添加操作到NSOperationQueue中

- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block
什么是并发数

同时执行的任务数
比如,同时开3个线程执行3个任务,并发数就是3
最大并发数的相关方法

- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
队列的取消、暂停、恢复

取消队列的所有操作

- (void)cancelAllOperations;

提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
暂停和恢复队列

- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
依赖

NSOperation之间可以设置依赖来保证执行顺序
设置依赖来保证执行顺序,可以设置不同队列中的opration创建的依赖关系

[operationB addDependency:operationA]; // 操作B依赖于操作A
使用,使用两个图片拼接这种典型
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
 [super viewDidLoad];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
 [self setUp];
}
- (void)setUp
{
 __block UIImage *image1=nil;
 __block UIImage *image2=nil;
 //创建一个异步队列
 NSOperationQueue *queue=[[NSOperationQueue alloc]init];

 //开启一个子线程下载图片
 NSOperation *op1=[NSBlockOperation blockOperationWithBlock:^{

     NSURL *url=[NSURL URLWithString:@"http://s.wasu.cn/data/images/201604/13/570e0b67c15fb.jpg"];

     NSData *date=[NSData dataWithContentsOfURL:url];

     image1=[UIImage imageWithData:date];

     NSLog(@"---op1----%@",[NSThread currentThread]);

 }];
 //下载另一个图片
 NSOperation *op2=[NSBlockOperation blockOperationWithBlock:^{

     NSURL *url=[NSURL URLWithString:@"http://i1.hdslb.com/bfs/archive/5e91addadc849572575b592814846977ab5559f1.jpg"];
     NSData *date=[NSData dataWithContentsOfURL:url];
     image2=[UIImage imageWithData:date];
     NSLog(@"---op2----%@",[NSThread currentThread]);

 }];

 //拼接图片
 NSOperation *op3=[NSBlockOperation blockOperationWithBlock:^{

     CGSize size=CGSizeMake(300, 400);

     UIGraphicsBeginImageContext(size);

     [image1 drawAsPatternInRect:CGRectMake(0, 0, 150,400)];

     [image2 drawAsPatternInRect:CGRectMake(150, 0, 150, 400)];

     UIImage * image=UIGraphicsGetImageFromCurrentImageContext();

     UIGraphicsEndImageContext();

     [[NSOperationQueue mainQueue]addOperationWithBlock:^{
         NSLog(@"进入主线程");
         self.imageView.image=image;
     }];
     NSLog(@"---op3----%@",[NSThread currentThread]);

 }];

 op1.completionBlock=^{

     NSLog(@"第一张图下载完毕");

 };

 op2.completionBlock=^{

     NSLog(@"第二张图下载完毕");
 };

 //依赖要放在添加队列之前
 [op3 addDependency:op1];
 [op3 addDependency:op2];

 NSArray *opreations=[NSArray arrayWithObjects:op1,op2,op3, nil];

 [queue addOperations:opreations waitUntilFinished:NO];
}
@end
output:
2016-06-27 17:35:39.651 图片合成[10336:2417737] ---op2----<NSThread: 0x7fe702c4c4e0>{number = 3, name = (null)}
2016-06-27 17:35:39.651 图片合成[10336:2417736] 第二张图下载完毕
2016-06-27 17:35:40.528 图片合成[10336:2417738] ---op1----<NSThread: 0x7fe702e80fa0>{number = 4, name = (null)}
2016-06-27 17:35:40.529 图片合成[10336:2417781] 第一张图下载完毕
2016-06-27 17:35:40.552 图片合成[10336:2417666] 进入主线程
2016-06-27 17:35:40.552 图片合成[10336:2417737] ---op3----<NSThread: 0x7fe702c4c4e0>{number = 3, name = (null)}
操作的监听

可以监听一个操作的执行完毕

- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
自定义NSOperation
自定义NSOperation的步骤:

重写- (void)main方法,在里面实现想执行的任务

重写- (void)main方法的注意点

自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应


GCD

Paste_Image.png Paste_Image.png
队列
Paste_Image.png
 dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
// 处理耗时操作的代码块... 
dispatch_async(dispatch_get_main_queue(), ^{  // 通知主线程刷新 
//回调或者说是通知主线程刷新, 
  }); 
});  
Paste_Image.png
上一篇 下一篇

猜你喜欢

热点阅读