美文网首页iOS面试
iOS之多线程(待续)

iOS之多线程(待续)

作者: SK丿希望 | 来源:发表于2017-10-26 16:27 被阅读19次

什么是多线程?

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

那什么是进程了?

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

什么线程?

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

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

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

在说下主线程吧?

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

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

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

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

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

  • 优点:轻量级,简单易用,可以直接操作线程对象
  • 缺点: 需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销。

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

  • 优点:是 Apple 开发的一个多核编程的解决方法,简单易用,效率高,速度快,基于C语言,更底层更高效,并且不是Cocoa框架的一部分,自动管理线程生命周期(创建线程、调度任务、销毁线程)。
  • 缺点: 使用GCD的场景如果很复杂,就有非常大的可能遇到死锁问题。

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

  • 优点:不需要关心线程管理,数据同步的事情,可以把精力放在学要执行的操作上。基于GCD,是对GCD 的封装,比GCD更加面向对象
  • 缺点: NSOperation是个抽象类,使用它必须使用它的子类,可以实现它或者使用它定义好的两个子类NSInvocationOperation、NSBlockOperation.

多线程的优缺点?

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

优点:

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
  • GCD的2个核心:任务,队列
  • 任务:即为在block中执行的代码
  • 队列:用来存放任务的
  • 注意事项:队列!=线程.队列中存放的任务最后都要由线程来执行.队列的原则:先进先出,后进后出(FIFO)
Paste_Image.png
队列
  • 队列分4种
    -- 串行队列:任务一个一个的执行
    -- 并发队列:任务并发执行
    -- 主队列:根主线程相关的队列,主队列的任务是在主线程中执行的
    -- 全局队列:一个特殊的并发队列
Paste_Image.png
  • 并发队列与全局队列的区别
    -- 并发队列有名称,可以跟踪错误.全局队列没有
    -- 在ARC中2个队列不需要考虑内存释放,但是在MRC中并发队列创建出来需要release操作
    -- 一般开发过程中我们使用全局队列
  • 同步和异步的区别
    -- 同步:(dispatch_sync)只能子安当前线程中执行任务,不具备开启新线程的能力
    -- 异步:(dispatch_async)可以在新的线程中执行任务,具备开启线程的能力
  • 各个队列的执行效果
    -- 同步串行队列:即在当前线程中顺序执行
    -- 异步串行队列:开辟一条新线程,在该线程中顺序执行
    -- 同步并发队列:不开辟新线程,在当前线程中顺序执行
    -- 异步并发队列:开辟多条线程,并且线程会重用,无序执行
    -- 异步主队列:不开辟新线程,顺序执行
    -- 同步主队列:会造成死锁(主线程与主队列相互等待,卡住主线程)
  • 线程间的通讯:(经典案例)子线程进行耗时操作(例下载更新等),回主线程刷新UI
 dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
// 处理耗时操作的代码块... 
dispatch_async(dispatch_get_main_queue(), ^{  // 通知主线程刷新 
//回调或者说是通知主线程刷新, 
  }); 
});  
Paste_Image.png

相关文章

  • iOS之多线程(待续)

    什么是多线程? 说白了就是CPU快速的在多条线程之间调度(即切换),且多条线程可以并发执行(即同时执行)提到多线程...

  • iOS开发之多线程(5)—— Pthreads

    文集 iOS开发之多线程(1)—— 概述iOS开发之多线程(2)—— ThreadiOS开发之多线程(3)—— G...

  • iOS开发之多线程(4)—— Operation

    文集 iOS开发之多线程(1)—— 概述iOS开发之多线程(2)—— ThreadiOS开发之多线程(3)—— G...

  • iOS开发之多线程(6)—— 线程安全与各种锁

    文集 iOS开发之多线程(1)—— 概述iOS开发之多线程(2)—— ThreadiOS开发之多线程(3)—— G...

  • iOS开发之多线程(1)—— 概述

    文集 iOS开发之多线程(1)—— 概述iOS开发之多线程(2)—— ThreadiOS开发之多线程(3)—— G...

  • iOS开发之多线程(2)—— Thread

    文集 iOS开发之多线程(1)—— 概述iOS开发之多线程(2)—— ThreadiOS开发之多线程(3)—— G...

  • iOS开发之多线程(3)—— GCD

    文集 iOS开发之多线程(1)—— 概述iOS开发之多线程(2)—— ThreadiOS开发之多线程(3)—— G...

  • iOS之多线程

    1.进程与线程的区别与联系 1.1.进程 广义上来讲,进程是系统中正在运行的一个应用程序,每个进程间都是独立的,每...

  • iOS之多线程

    进程和线程 首先,在了解多线程之前要了解什么是进程,什么是线程 什么是进程呢?进程是指在系统中正在运行的一个应用程...

  • iOS 之多线程

    一. 多线程概述 程序:由源代码生成的可执行应用进程:一个正在进行的程序.线程:程序中独立的代码片段. 注:一个进...

网友评论

    本文标题:iOS之多线程(待续)

    本文链接:https://www.haomeiwen.com/subject/olshpxtx.html