多线程的概念,多线程技术在iOS开发中的使用

2019-08-20 22:19 来源:未知

进程和线程

多线程是一个比较轻量级的方法来实现单个应用程序内多个代码执行路径

1.什么是进程?

进程是指在一个系统内正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。比如同时打开两个应用支付宝和微信,系统就会分别启动2个进程。

 

在系统级别内,程序并排执行,程序分配到每个程序的执行时间是基于该程序的所需时间和其他程序的所需时间来决定的。

2.什么是线程?

1个进程如果想要执行任务,必须要有线程(每一个进程至少要有一个线程)

线程是进程的基本执行单元,一个进程(程序)的所有任务必须要在线程中执行

要使用多线程,首先需要理解进程和线程这2个概念。这里我简单的说一下。

然而,在每个程序内部,存在一个或者多个执行线程,它同时或在一个几乎同时发生的方式里执行不同的任务。

3.线程的串行

一个线程中的任务的执行是串行的,如果要在一个线程中执行多个任务,只能一个一个地桉顺序执行;换句话说,在同一时间内,一个线程只能执行一个任务。

 

概要提示:

4.什么是多线程

一个进程中可以开启多条线程,每条线程并行(同时)执行不同的任务

优点:多线程可以提供程序的执行效率

所谓进程对应的是一个应用程序,负责开辟内存空间供应用程序使用,但是进程不能执行任务(指令)。一个进程至少包含一条线程,线程是程序的执行流。

iPhone中的线程应用并不是无节制的,官方给出的资料显示,iPhone OS下的主线程的堆栈大小是1M,第二个线程开始就是512KB,并且该值不能通过编译器开关或线程API函数来更改,只有主线程有直接修改UI的能力

5.多线程的原理

同一时间,CPU只能处理一条线程,只有一条线程在工作(执行),当多线程并发(同时)执行,实质也就是CPU快速地在多条线程之间调度(切换)

如果CPU调度线程的时间足够快,就会造成了多线程并发执行的假象。如果线程非常的多,CPU将会在N多线程之间调度,CPU会被活活累死,消耗大量的CPU资源,这样也就导致了每条线程被调度执行的频次会降低,即线程的执行效率降低

 

一、线程概述

6.多线程的优点:

不仅能适当提高程序的执行效率,还能提高资源利用率,如CPU、内存的利用率

•iOS程序启动时,在创建一个进程的同时, 会开始运行一个线程,该线程被称为主线程

有些程序是一条直线,起点到终点——如简单的hello world,运行打印完,它的生命周期便结束了,像是昙花一现。

7.多线程的缺点:

开启多线程需要占用一定的内存空间:默认情况下,主线程占用1M,子线程占用512KB,如果开启大量的线程,或占用大量的内存空间,降低程序的性能。如果线程的数量很多,不止会使CPU在调度线程上的开销增大,也会使得程序设计更加复杂:如线程间的通信和数据共享等。

•系统中的每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则共用进程的内存空间

有些程序是一个圆,不断循环直到将它切断——如操作系统,一直运行直到你关机。

8.多线程的应用:

主线程:一个iOS程序运行后,默认会开启一条线程,这条线程就被称为"主线程"和"UI线程"

主线程的作用:显示刷新UI界面以及处理UI事件(如:点击事件、滚动事件、拖拽事件等)

主线程的使用注意事项:不要把比较耗时的操作放到主线程中。因为耗时的操作会卡住主线程,对UI的流畅度造成严重的影响,给用户一种"卡"的坏体验

•每创建一个新的线程,都会消耗一定内存和CPU时间

一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。

//8.1多线程的安全隐患

•当多个线程对同一个资源出现争夺的时候需要注意线程安全问题

Mac和IOS中的程序启动,创建好一个进程的同时,一个线程便开始运作,这个线程叫做主线程。主线成在程序中的位置和其他线程不同,它是其他线程最终的父线程,且所有的界面的显示操作即AppKit或UIKit的操作必须在主线程进行。

//8.1.1资源共享:

一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。如多个线程访问同一个对象或者同一个变量,再或者是同一个文件。当多个线程访问同一块资源时,很容易造成引发数据错乱和数据安全的问题

 

系统中每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则公用进程的内存空间。

8.1.2-----解决办法

互斥锁使用格式---

@synchronized (锁对象) {

//需要锁定的代码

}

注意:锁定一份代码只用一把锁,用多把锁是无效的

多线程的应用及使用注意事项

每创建一个新的进成,都需要一些内存(如每个线程有自己的stack空间)和消耗一定的CPU时间。

互斥锁的优缺点:

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

缺点:需要消耗大量的CPU资源

互斥锁使用的前提是:多条线程去抢夺同一块资源

将耗时、轮询或者并发需求高等任务分配到其他线程执行,并由主线程负责统一更新界面会使得应用程序更加流畅,用户体验更好,例如网络请求,播放游戏的背景音乐等。

当多个进成对同一个资源出现争夺的时候需要注意线程安全问题

9.创建线程

1.一个NSThread对象就代表一条线程,

•线程使用不是无节制的

创建线程

1.1->创建和启动线程

//创建线程

NSThread* thread = [[NSThread alloc]initWithTarget:self selector:@selector(initThread) object:nil];

//启动线程

[thread start];

//特点:线程一启动,就会在线程thread中执行self的initThread方法

–iOS中的主线程的堆栈大小是1M

创建一个新的线程就是给进程增加一个执行流,所以新建一个线程需要提供一个函数或者方法作为线程的进口。

//2--主线程的相关用法:

//2.1-------------是否为主线程

//    @property (readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);

//thread.isMainThread

//2.2-------------是否为主线程

//    @property (class, readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main

//属性用法    [NSThread isMainThread];

//2.3-------------获得主线程

//    @property (class, readonly, strong) NSThread *mainThread NS_AVAILABLE(10_5, 2_0);

//属性用法[NSThread mainThread];

//2.4---线程的调度优先级:调度优先级的取值范围是0.0~1.0,默认0.5,值越大,优先级越高

//    (double)threadPriority;

//    (BOOL)setThreadPriority:(double)p;

//设置线程的名字

// 2.5---  @property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);

//3-----其他创建线程的方式

–从第二个线程开始都是512KB

1.使用NSThread

//3.1----创建线程后自动启动线程

// (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

//3.2----隐式创建并启动线程

//    [self performSelectorInBackground:@selector(initThread) withObject:nil];

//上述2种创建线程的优缺点:优点是简单快捷,缺点是无法对线程进行更详细的设置。

当前流程继续performSelector关于内存管理的执行原理是这样的执行 [self performSelector:@selector(method1:) withObject:self.tableLayer afterDelay:3]; 的时候,系统会将tableLayer的引用计数加1,执行完这个方法时,还会将tableLayer的引用计数减1,由于延迟这时tableLayer的引用计数没有减少到0,也就导致了切换场景dealloc方法没有被调用,出现了内存泄露。

利用如下函数:

[NSObject cancelPreviousPerformRequestsWithTarget:self]

当然你也可以一个一个得这样用:

[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(method1:) object:nil]

加上了这个以后,顺利地执行了dealloc方法

–这些数值不能通过编译器开关或线程API函数更改

NSThread提供了创建线程的路径,还可以提供了监测当前线程是否是主线程的方法

4. 线程间的通信

4.1---线程间通信的含义:在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信

4.2---线程间通信的体现:一个线程给另一个线程传递数据或者一个线程执行完之后去执行另一个线程

4.3---- 线程间通信的常用方法

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

4.4---从子线程回到主线程

dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 执⾏耗时的异步操作...

dispatch_async(dispatch_get_main_queue(), ^{

// 回到主线程,执⾏UI刷新操作

});

});

或者

dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 执⾏耗时的异步操作...

// 回到主线程,执⾏UI刷新操作

[self.imageView performSelectorOnMainThread:@selector(方法) withObject:image waitUntilDone:NO];

});

注:更新UI界面,处理界面和用户之间的交互事件一定要在主线程中处理。

使用NSThread创建一个新的线程有两种方式:

5.GCD:Grand Central Dispatch

5.1--GCD是纯C语言,提供了非常强大的函数

 

1.创建一个NSThread的对象,调用Start方法——使用一个目标对象的方法初始化一个NSThread对象,或者创建一个继承自NSThread的子类,实现起main方法?,然后在直接创建这个子类的对象。

5.2---GCD的优势:

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

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

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

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

5.3-------

iOS中的多线程技术

2.使用detachNewThreadSelector:toTarget:withObject:这个类方法创建一个子线程,这个比较直接,直接使用目标对象的方法作为线程启动入口

GCD的两个核心概念:

5.3.1--任务:执行什么操作

5.3.2--队列:用来存放任务

GCD的使用就两个步骤:

1->定制任务

2->确定想要做的事情

将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行

重点:任务的取出遵循队列的FIFO原则:先进先出,后进后出

 

2.使用NSObject

5.4---执行任务

5.4.1----GCD中有两个用来执行任务的函数。特别说明:把右边的参数(任务-<#^(void)block#>)提交给左边的参数(队列:<#dispatch_queue_t  _Nonnull queue#>)进行执行

第一种:用同步的方式执行任务    dispatch_sync(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

参数说明:queue:队列        block:任务

第二种:用异步的方式执行任务dispatch_async(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)

1.NSObject的多线程方法-对NSThread的封装(一个NSThread对象对应一条线程)

使用NSObject直接就加入了对多线程的支持,允许对象的某个方法在后台运行。

5.4.2---同步和异步的区别

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

异步:在另一条线程中执行任务,它具备了开启新线程的能力

 

[my0bj performSelectorInBackground:@selector(doSomething) withObject:nil];

5.5---队列

GCD的队列类型可以分为两种类型:

1->并发队列:可以让多个任务并发(同时)执行(会自动开启多个线程同时执行任务),并发功能只有在异步:dispatch_async函数下才是有效的

GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建

使用dispatch_get_global_queue函数获得全局的并发队列

dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); // 此参数暂时无用,用0即可

示例:

这个参数是留给以后用的,暂时用不上,传个0。

第一个参数为优先级,这里选择默认的。获取一个全局的默认优先级的并发队列。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列

说明:全局并发队列的优先级

#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)

#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低

#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台

2->串行队列:让任务一个接着一个的执行,即一个任务执行完了,再去执行另一个任务

GCD中获得串行有两种方法:

1.1->使用dispatch_queue_create函数创建串行队列

dispatch_queue_t  dispatch_queue_create(const char *label,  dispatch_queue_attr_t attr); // 队列名称, 队列属性,一般用NULL即可

队列名称的作用:

将来调试的时候,可以看得出任务是在哪个队列中执行的。

示例:

dispatch_queue_t queue = dispatch_queue_create("wendingding", NULL); // 创建

dispatch_release(queue); // 非ARC需要释放手动创建的队列

1.2-> 使用主队列(跟主线程相关联的队列)

主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行

使用dispatch_get_main_queue()获得主队列

示例:dispatch_queue_t queue = dispatch_get_main_queue();

•开启后台线程执行任务的方法

3.POSIX Thread

小结

说明:同步函数不具备开启线程的能力,无论是什么队列都不会开启线程;异步函数具备开启线程的能力,开启几条线程由队列决定(串行队列只会开启一条新的线程,并发队列会开启多条线程)。

-(void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg

由于Mac和IOS都是基于Darwin系统,Darwin系统的UNX内核,是基于mach和BSD的,继承了BSD的POSIX接口,所以可以直接使用POSIX线程的相关接口开实现线程

//iOS的延时操作

/*

iOS常见的延时操作有两种方法:

1.调用NSObject的方法: [self performSelector:@selector(initThread) withObject:nil afterDelay:2.0];

2秒后调用self的initThread方法,该方法在那个线程调用,那么run就在哪个线程执行(当前线程),通常是主线程。

2.使用GCD函数

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

//2秒后执行这里

});

如果队列是主队列,那么就在主线程执行,如果队列是并发队列,那么会新开启一个线程,在子线程中执行。

/*

•在后台线程中通知主线程执行任务的方法

创建线程的接口为 pthread_create, 当然在创建线程之前可以创建好相关线程的属性

NSOperation

1.---- NSOperation的作用:配合使用NSOperation和NSOperationQueue也能实现多线程编程

其实现多线程的具体步骤:

1.1->将要执行的操作封装到一个NSOperation对象中

1.2->将NSOperation对象添加到NSOperationQueue中

1.3->系统会自动将NSOperationQueue中的NSOperation取出来

1.4->将取出的NSOperation封装的操作放到一条新线程中执行

–(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

——————————————————————————————————————

2----NSOperation的子类

NSOperation是一个抽象类,并不具备封装操作的能力,一定要使用他的子类才可以

使用NSOperationi子类的方式有三种:

(1)NSInvocationOperation

(2)NSBlockOperation

(3)自定义子类继承NSOperation,实现内部相应的⽅法

•获取线程信息

NSOperation&NSOperationQueue

3.---创建操作对象,封装要执行的任务

3.1->NSInvocationOperation

NSInvocationOperation* operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(initThread) object:nil];

//执行操作

[operation start];

//说明:一旦执行操作,就会调用target的initThread方法

重点:操作对象默认在主线程中执行,只有添加到队列中才会开启新的线程。即默认情况下,如果操作没有放到队列中queue中,都是同步执行。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作

3.2->--NSBlockOperation

//封装操作

//创建NSBlockOperation操作对象

NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{

}];

//添加操作

[operation addExecutionBlock:^{

}];

//开启执行操作

[operation start];

//重点:只要NSBlockOperation封装的操作数大于一,就会异步执行操作

4.NSOperationQueue

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

NSBlockOperation* operation = [NSBlockOperation blockOperationWithBlock:^{

}];

//添加操作

[operation addExecutionBlock:^{

}];

//创建NSOperationQueue

NSOperationQueue* queue = [[NSOperationQueue alloc]init];

//把操作添加到队列中

//第一种添加方式:

[queue addOperation:operation];

//第二种添加方式:

[queue addOperationWithBlock:^{

}];

重点:系统自动将NSOperationqueue中的NSOperation对象取出,将其封装的操作放到一条新的线程中执行。

[NSThread currentThread];

很多时候我们使用多线程,需要控制线程的并发数,毕竟线程也是需要消耗系统资源的,当程序中同时运行的线程过多时,系统必然变慢,所以很多时候我们会控制同时运行线程的数目

NSOperation基本操作

1.->并发数:同时执行的任务数,如果同时开启3个线程执行3个任务,那并发数就是3了

2-> 最大并发数:同一时间最多只能执行的任务个数

3-> 最大并发数的相关方法

- (NSInteger)maxConcurrentOperationCount;

- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

特别说明:如果没有设置最大并发数,那么并发的个数会有系统内存和CPU决定,

温馨提示:最大并发数不要乱写,最好5以内,一般以2~3为宜,因为虽然任务是在子线程进行处理的,但是CPU处理这些过多的子线程可能会影响UI,让UI变卡。

•线程休眠      

NSOperation可以封装我们的操作,然后将创建好的NSOperation对象放到NSOperationQueue队列中,OperationQueue便开始启动新的线程去执行队列中的操作,OperationQueue的并发数时可以通过如下方式进行设置的:

队列的取消,暂停和恢复

1->取消队列的所有操作

- (void)cancelAllOperations;

提⽰:也可以调用NSOperation的- (void)cancel⽅法取消单个操作

2->暂停和恢复队列

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

- (BOOL)isSuspended; //当前状态

3->暂停和恢复的适用场合:在tableview界面,开线程下载远程的网络界面,对UI会有影响,使用户体验变差。那么这种情况,就可以设置在用户操作UI(如滚动屏幕)的时候,暂停队列(不是取消队列),停止滚动的时候,恢复队列。

[NSThread sleepForTimeInterval:1.0f];

- (void)setMaxConcurrentOperationCount:(NSInteger)count

操作优先级

1->设置NSOperation在queue中的优先级,可以改变操作的执⾏优先级

- (NSOperationQueuePriority)queuePriority;

- (void)setQueuePriority:(NSOperationQueuePriority)p;

(2)优先级的取值

NSOperationQueuePriorityVeryLow = -8L,

NSOperationQueuePriorityLow = -4L,

NSOperationQueuePriorityNormal = 0,

NSOperationQueuePriorityHigh = 4,

NSOperationQueuePriorityVeryHigh = 8

说明:优先级高的任务,调用的几率会更大。

提示:

GCD时Grand central Dispatch的缩写,是一系列BSD层面的接口。在mac10.6和IOS4.0以后才引入的

操作依赖

NSOperation之间可以设置依赖来保证执行顺序,⽐如一定要让操作A执行完后,才能执行操作B,可以像下面这么写

[operationB addDependency:operationA]; // 操作B依赖于操作

但是不能循环依赖

重点:任务添加的顺序并不能够决定执行顺序,执行的顺序取决于依赖。使用Operation的目的就是为了让开发人员不再关心线程。

–performSelectorInBackground方法本身是在主线程中执行的,而选择器指定的方法是在后台线程中进行的

且现在NSOperation和NSOperationQueue的多线程的实现就是基于GCD的。目前这个特性也被移植到 FreeBSD上了,可以查看libdispatch这个开源项目。

操作的监听:

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

- (void (^)(void))completionBlock;

- (void)setCompletionBlock:(void (^)(void))block;

第一种方式:可以直接跟在任务后面编写需要完成的操作,如这里在下载图片后,紧跟着下载第二张图片。但是这种写法有的时候把两个不相关的操作写到了一个代码块中,代码的可阅读性不强。

第二种方式:  在上一个任务执行完后,会执行operation.completionBlock=^{}代码段,且是在当前线程执行。

*/

–尽管使用performSelectorInBackground方法调用的任务可以更新UI界面,但是在实际开发中,涉及到UI界面的更新操作,还是要使用performSelectorOnMainThread方法,以避免不必要的麻烦

 

注:

 

•内存管理对于多线程非常重要

 

•Objective-C可以凭借@autoreleasepool使用内存资源,并需要时回收资源

 

•每个线程都需要有@autoreleasepool,否则可能会出现内存泄漏

多线程的优缺点

 

 

2.NSThread

多线程的优点

 

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

•创建线程方法:

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

1. (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;

 

2.- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;

多线程的缺点

•参数说明:

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

–selector:线程执行的方法,只能有一个参数,不能有返回值

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

–target:selector消息发送的对象

程序设计更加复杂:比如线程之间的通信、多线程的数据共享

–argument:传输给target的唯一参数,也可以是nil

 

 

多线程在iOS开发中的应用

3.NSOperation/NSOperationQueue

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

 

主线程的主要作用

•NSOperation的两个子类

显示刷新UI界面

1.NSInvocationOperation

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

2.NSBlockOperation

 

 

主线程的使用注意:别将比较耗时的操作放到主线程中。

•工作原理:

耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验

1.用NSOperation封装要执行的操作

 

2.将创建好的NSOperation对象放NSOperationQueue中

 

3.启动OperationQueue开始新的线程执行队列中的操作

IOS的多线程,一般分为三种方式:

•注意事项:

1,Thread;
2, Cocoa operations;
3, Grand Central Dispatch (GCD) (iOS4 才开始支持)

1.使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢

下面简单说明一下:

2.使用以下方法可以控制并发的线程数量:

1:NSThread

-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;

创建方式主要有两种:


[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

NSThread* myThread = [[NSThread alloc] initWithTarget:self
selector:@selector(myThreadMainMethod:)
object:nil];
[myThread start]; //启动线程

 

这两种方式的区别是:前一种一调用就会立即创建一个线程来做事情;而后一种虽然你 alloc 了也 init了,但是要直到我们手动调用 start 启动线程时才会真正去创建线程。这种延迟实现思想在很多跟资源相关的地方都有用到。后一种方式我们还可以在启动线程之前,对线程进行配置,比如设置 stack 大小,线程优先级。

此外还有一种间接的方式:利用NSObject的方法

performSelectorInBackground:withObject: 来创建一个线程:
[myObj performSelectorInBackground:@selector(myThreadMainMethod) withObject:nil]; //在后台运行某一个方法
其效果与 NSThread 的 detachNewThreadSelector:toTarget:withObject: 是一样的。

3.使用addDependency可以建立操作之间的依赖关系,设定操作的执行顺序

 

 

2,NSOperation:

 

 

上面的程序设置依赖关系后,只有等操作a和b都执行完,才会执行c.

官方解释:The NSOperation class is an abstract class you use to encapsulate the code and data associated with a single task. Because it is abstract, you do not use this class directly but instead subclass or use one of the system-defined subclasses (NSInvocationOperation or NSBlockOperation) to perform the actual task.

 

并发执行你需要重载如下4个方法

4.GCD

//执行任务主函数,线程运行的入口函数

 

-(void)start

•GCD队列:

//是否允许并发,返回YES,允许并发,返回NO不允许。默认返回NO

1.全局队列:所有添加到主队列中的任务都是并发执行的(可能会开启多条线程)

-(BOOL)isConcurrent

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

- (BOOL)isExecuting

2.串行队列:所有添加到串行队列中的任务都是顺序执行的(只可能会开启一条线程)

//是否已经完成,这个必须要重载,不然放在放在NSOperationQueue里的NSOpertaion不能正常释放。

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);

- (BOOL)isFinished

3.主队列:所有添加到主队列中的任务都是在主线程中执行的

 

dispatch_get_main_queue();

比如TestNSOperation:NSOperaion 重载上述的4个方法,

 

声明一个NSOperationQueue,NSOperationQueue *queue = [[[NSOperationQueue alloc ] init]autorelease];

•异步操作

[queue addOperation:testNSoperation];

–dispatch_async 在其他线程执行任务,会开启新的线程

它会自动调用TestNSOperation里的start函数,如果需要多个NSOperation,你需要设置queue的一些属性,如果多个NSOperation之间有依赖关系,也可以设置,具体可以参考API文档。

–异步方法无法确定任务的执行顺序

 

(2)非并发执行

•同步操作

-(void)main

–dispatch_sync 在当前在线程执行任务,不开启新的线程

只需要重载这个main方法就可以了。

–同步操作与队列无关

3、 GCD

–同步方法会依次执行,能够决定任务的执行顺序

 

–更新界面UI时,最好使用同步方法

 

dispatch_async(dispatch_queue_t queue,dispatch_block_t block);

async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.

 

之所以程序中会用到多线程是因为程序往往会需要读取数据,然后更新UI.为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程.GCD里就有三种queue来处理。

  1. Main queue:

  顾名思义,运行在主线程,由dispatch_get_main_queue获得.和ui相关的就要使用MainQueue.

2.Serial quque(private dispatch queue)

  每次运行一个任务,可以添加多个,执行次序FIFO. 通常是指程序员生成的.

  1. Concurrent queue(globaldispatch queue):

可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.

所以我们可以大致了解使用GCD的框架:

1

2

3

4

5

6

7

dispatch_async(getDataQueue,^{

//获取数据,获得一组后,刷新UI.

dispatch_aysnc(mainQueue, ^{

//UI的更新需在主线程中进行

};

}

)

下面 就来总结一下这三种多线程方式的区别吧:

Thread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,你需要自己管理thread的生命周期,线程之间的同步。线程共享同一应用程序的部分内存空间, 它们拥有对数据相同的访问权限。你得协调多个线程对同一数据的访问,一般做法是在访问之前加锁,这会导致一定的性能开销。在 iOS 中我们可以使用多种形式的 thread:

Cocoa threads: 使用NSThread 或直接从 NSObject 的类方法 performSelectorInBackground:withObject: 来创建一个线程。如果你选择thread来实现多线程,那么 NSThread 就是官方推荐优先选用的方式。

Cocoa operations是基于 Obective-C实现的,类 NSOperation 以面向对象的方式封装了用户需要执行的操作,我们只要聚焦于我们需要做的事情,而不必太操心线程的管理,同步等事情,因为NSOperation已经为我 们封装了这些事情。 NSOperation 是一个抽象基类,我们必须使用它的子类。iOS 提供了两种默认实现:NSInvocationOperation 和 NSBlockOperation。

Grand Central Dispatch (GCD): iOS4 才开始支持,它提供了一些新的特性,以及运行库来支持多核并行编程,它的关注点更高:如何在多个 cpu 上提升效率。

 

最后,既然说道多线程的开发,难免会在多线程之间进行通讯;

利用NSObject的一些类方法,可以轻松搞定。

 

在应用程序主线程中做事情:

  • (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait

 

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array

在指定线程中做事情:

  • (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait

 

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array

在当前线程中做事情:

//Invokes a method of the receiver on the current thread using the default mode after a delay.

- (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay

 

performSelector:withObject:afterDelay:inModes:

取消发送给当前线程的某个消息
cancelPreviousPerformRequestsWithTarget:

 

cancelPreviousPerformRequestsWithTarget:selector:object:

如在我们在某个线程中下载数据,下载完成之后要通知主线程中更新界面等等,可以使用如下接口:- (void)myThreadMainMethod
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// to do something in your thread job
...
[self performSelectorOnMainThread:@selector(updateUI) withObject:nil waitUntilDone:NO];
[pool release];
}

 

注:同步还是异步,取决于方法名(不影响主队列,影响全局队列、串行队列)。

要使用多线程,首先需要理解进程和线程这2个概念。这里我简单的说一下。 所谓进程对应的是一个应用程序,负责开辟内存空间供...

TAG标签:
版权声明:本文由吉利彩票平台注册-吉利彩票平台官方注册-官网推荐发布于信息公开,转载请注明出处:多线程的概念,多线程技术在iOS开发中的使用