iOS 多线程NSThread,GCD,NSOperation

单例模式例子:

https://github.com/XiaoRuiZuo/Singleton

多线程:
多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

3.多线程

多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。

原理:

同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)

多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)

如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象

注意:多线程并发,并不是cpu在同一时刻同时执行多个任务,只是CPU调度足够快,造成的假象。

优点:

能适当提高程序的执行效率

能适当提高资源利用率(CPU、内存利用率)

缺点:

1.开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能

2.线程越多,CPU在调度线程上的开销就越大

二、iOS开发中的应用

1.主线程

一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”。

作用:

显示\刷新UI界面

处理UI事件(比如点击事件、滚动事件、拖拽事件等)

注意:

刷新UI必须放在主线程

别将比较耗时的操作放到主线程中

耗时操作会卡住主线程,严重影响UI的流畅度

2.实现方案

NSThread

一个NSThread对象控制执行的线程。当你想在自己的执行线程的Objective-C的方法运行使用这个类。当你需要执行一个漫长的任务线程是特别有用的,但不希望它阻止应用程序的其余部分的执行。特别是,您可以使用线程来避免阻塞应用程序,它处理的用户界面和事件相关的操作的主线。线程也可以用来将一个大的工作分成几个较小的作业,这可能会导致在多核计算机性能的提高。

一、创建和启动线程

// 1.创建线程NSThread*thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(run) object:nil];// 2.启动线程[thread start];// 线程一启动,就会在线程thread中执行self的run方法// 创建线程后自动启动线程[NSThreaddetachNewThreadSelector:@selector(run) toTarget:selfwithObject:nil];// 隐式创建并启动线程[selfperformSelectorInBackground:@selector(run) withObject:nil];上述2种创建线程方式的优缺点- 优点:简单快捷- 缺点:无法对线程进行更详细的设置

二、主线程相关用法

// 返回主线程+ (NSThread*)mainThread;// 是否为主线程(类方法)+ (BOOL)isMainThread;// 是否为主线程(对象方法)- (BOOL)isMainThread;

三、其他用法

// 线程通知NSDidBecomeSingleThreadedNotificationNSThreadWillExitNotificationNSWillBecomeMultiThreadedNotification// 获得当前线程NSThread*current = [NSThreadcurrentThread];// 线程的名字- (void)setName:(NSString*)n; - (NSString*)name;

线程的状态

线程的状态

// 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态- (void)start;// 阻塞(暂停)线程->进入阻塞状态+ (void)sleepUntilDate:(NSDate*)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti;// 强制停止线程-> 进入死亡状态+ (void)exit;注意:一旦线程停止(死亡)了,就不能再次开启任务

多线程的安全隐患

一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。

当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。

解决办法:(互斥锁)

// 注意:锁定1份代码只用1把锁,用多把锁是无效的@synchronized(锁对象) {// 需要锁定的代码  }

优点与缺点

能有效防止因多线程抢夺资源造成的数据安全问题

需要消耗大量的CPU资源

atomic与nonatomic

OC在定义属性时有nonatomic和atomic两种选择

@property(nonatomic,copy)NSString*name;@property(atomic,copy)NSString*name;

atomic:原子属性,为setter方法加锁(默认就是atomic)

线程安全,需要消耗大量的资源

nonatomic:非原子属性,不会为setter方法加锁

非线程安全,适合内存小的移动设备

开发建议

所有属性都声明为nonatomic

尽量避免多线程抢夺同一块资源

尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

线程间通信

在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信

例如在子线程下载图片,在主线程刷新UI显示图片。

线程间通信常用方法

// 1.在主线程上执行操作- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;// 2.在指定线程上执行操作- (void)performSelector:(SEL)aSelector onThread:(NSThread*)thr withObject:(id)arg waitUnti


GCD

全称是Grand Central Dispatch,“伟大的中枢调度器”

GCD是苹果公司为多核的并行运算提出的解决方案

纯C语言,提供了非常多强大的函数

优势

GCD会自动利用更多的CPU内核(比如双核、四核)

GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

基本概念

任务和队列

GCD中有2个核心概念

1.任务:执行什么操作

2.队列:用来存放任务

// 1.定制任务:确定想做的事情// 2.将任务添加到队列中:GCD会自动将队列中的任务取出,放到对应的线程中执行。Tips:任务的取出遵循队列的FIFO原则:先进先出,后进后出

任务

一、执行任务

-queue:队列 - block:任务// 1.用同步的方式执行任务dispatch_sync(dispatch_queue_tqueue,dispatch_block_tblock);// 2.用异步的方式执行任务dispatch_async(dispatch_queue_tqueue,dispatch_block_tblock);// 3.GCD中还有个用来执行任务的函数// 在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行dispatch_barrier_async(dispatch_queue_tqueue,dispatch_block_tblock);

注意:

同步:只能在当前线程中执行任务,不具备开启新线程的能力

异步:可以在新的线程中执行任务,具备开启新线程的能力

队列

一、并发队列(Concurrent Dispatch Queue)

可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)

并发功能只有在异步(dispatch_async)函数下才有效

// 1.使用dispatch_queue_create函数创建队列dispatch_queue_tdispatch_queue_create(constchar*label,// 队列名称dispatch_queue_attr_tattr);// 队列的类型// 2.创建并发队列dispatch_queue_tqueue= dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);// 3.使用dispatch_get_global_queue函数获得全局的并发队列dispatch_queue_tdispatch_get_global_queue(dispatch_queue_priority_tpriority,unsignedlongflags);// dispatch_queue_priority_t priority(队列的优先级 )// unsigned long flags( 此参数暂时无用,用0即可 )// 4.获得全局并发队列dispatch_queue_tqueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);// 5.全局并发队列的优先级#defineDISPATCH_QUEUE_PRIORITY_HIGH2// 高#defineDISPATCH_QUEUE_PRIORITY_DEFAULT0// 默认(中)#defineDISPATCH_QUEUE_PRIORITY_LOW (-2)// 低#defineDISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN// 后台

二、串行队列(Serial Dispatch Queue)

让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

// 1.使用dispatch_queue_create函数创建串行队列// 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)dispatch_queue_tqueue= dispatch_queue_create("queue",NULL);// 2.使用dispatch_get_main_queue()获得主队列dispatch_queue_tqueue= dispatch_get_main_queue();注意:主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行。

三、各种队列的执行效果

特别注意:使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列 (线程卡死)

新手易混淆

有4个术语比较容易混淆:同步、异步、并发、串行

1.同步和异步主要影响:能不能开启新的线程

同步:只是在当前线程中执行任务,不具备开启新线程的能力

异步:可以在新的线程中执行任务,具备开启新线程的能力

2.并发和串行主要影响:任务的执行方式

并发:多个任务并发(同时)执行

串行:一个任务执行完毕后,再执行下一个任务

GCD运用

一、线程间通信

从子线程回到主线程dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 执行耗时的异步操作...dispatch_async(dispatch_get_main_queue(), ^{// 回到主线程,执行UI刷新操作});});

二、延时执行

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

// 2秒后异步执行这里的代码...

});

三、一次性代码

// 使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{// 只执行1次的代码(这里面默认是线程安全的)});

四、快速迭代

// 使用dispatch_apply函数能进行快速迭代遍历dispatch_apply(10, dispatch_get_global_queue(0,0), ^(size_tindex){// 执行10次代码,index顺序不确定});

五、队列组

// 分别异步执行2个耗时的操作、2个异步操作都执行完毕后,再回到主线程执行操作dispatch_group_tgroup=  dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 执行1个耗时的异步操作});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 执行1个耗时的异步操作});dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 等前面的异步操作都执行完毕后,回到主线程...});

单例模式

作用:

可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问。从而方便地控制了实例个数,并节约系统资源

使用场合

在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)

实现过程:

1.重写实现:

创建一个需要单例模式的文件

// 1.在.m中保留一个全局的static的实例staticid_instance;// 2.重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)+ (instancetype)allocWithZone:(struct_NSZone*)zone{staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{        _instance = [superallocWithZone:zone];    });return_instance;}// 3.提供1个类方法让外界访问唯一的实例+ (instancetype)sharedInstance{staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{        _instance = [[selfalloc] init];    });return_instance;}// 4.实现copyWithZone:方法- (id)copyWithZone:(struct_NSZone*)zone{return_instance;}

2.宏实现:

// .h文件#define SingletonH(name) + (instancetype)shared##name;// .m文件#define SingletonM(name)\static id _instance;\\+ (instancetype)allocWithZone:(struct _NSZone *)zone\{\static dispatch_once_t onceToken;\dispatch_once(&onceToken, ^{\_instance =[super allocWithZone:zone];\});\return _instance;\}\\+ (instancetype)shared##name\{\static dispatch_once_t onceToken;\dispatch_once(&onceToken, ^{\_instance =[[self alloc]init];\});\return _instance;\}\\- (id)copyWithZone:(NSZone *)zone\{\return _instance;\}


NSOperation

NSOperation是苹果封装的一套多线程的东西,不像GCD是纯C语言的,这个是OC的。但相比较之下GCD会更快一些,但本质上NSOPeration是多GDC的封装。

NSOperation相对于GCD:

NSOperation拥有更多的函数可用

NSOperationQueue中,可以建立各个NSOperation之间的依赖关系。

NSOperationQueue支持KVO。可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)

GCD 只支持FIFO 的队列,而NSOperationQueue可以调整队列的执行顺序

NSOperation剖析

NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类

使用NSOperation子类的方式有3种

NSInvocationOperation

NSBlockOperation

自定义子类继承NSOperation,实现内部相应的方法

NSOperationQueue

1.NSOperation可以调用start方法来执行任务,但默认是同步执行的

2.如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

// 添加操作到NSOperationQueue中- (void)addOperation:(NSOperation*)op;- (void)addOperationWithBlock:(void(^)(void))block;

NSOperation的使用

配合使用NSOperation和NSOperationQueue就能实现多线程编程

具体步骤:

1.将需要执行的操作封装到一个NSOperation对象中

2.将NSOperation对象添加到NSOperationQueue中

3.系统会自动将NSOperationQueue中的NSOperation取出来放到一条新线程中执行

NSInvocationOperation子类

// 1.创建NSInvocationOperation对象- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;// 2.调用start方法开始执行操作- (void)start;// 一旦执行操作,就会调用target的sel方法

注意:

默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作。

只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作

NSBlockOperation子类

// 1.创建NSBlockOperation对象+ (id)blockOperationWithBlock:(void(^)(void))block;// 2.通过addExecutionBlock:方法添加更多的操作- (void)addExecutionBlock:(void(^)(void))block;

注意:

只要NSBlockOperation封装的操作数 > 1,就会异步执行操作

自定义NSOperation

// 1.创建对象继承NSOperation,重写- (void)main;// 在里面实现想执行的任务

注意:

自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)

经常通过

-(BOOL)isCancelled

方法检测操作是否被取消,对取消做出响应。

NSOperation方法

一、最大并发数

可以通过对最大并发数设置,控制程序中线程的数量

// 1.最大并发数的相关方法- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

二、取消、暂停、恢复

// 1.取消队列的所有操作- (void)cancelAllOperations;// 2.取消单个操作- (void)cancel// 暂停- (void)setSuspended:(BOOL)b;// YES代表暂停队列,NO代表恢复队列// 恢复队列- (BOOL)isSuspended;

三、依赖

NSOperation之间可以设置依赖来保证执行顺序

// 1.比如一定要让操作A执行完后,才能执行操作B,可以这么写[operationB addDependency:operationA];// 操作B依赖于操作A注意:可以在不同queue的NSOperation之间创建依赖关系

四、操作的监听

// 1.可以监听一个操作的执行完毕- (void(^)(void))completionBlock;- (void)setCompletionBlock:(void(^)(void))block;

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

推荐阅读更多精彩内容