iOS之多线程(待续)

什么是多线程?

说白了就是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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容