多线程
- Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么?
1.线程创建有三种方法:
使用NSThread创建、
使用GCD的dispatch、
使用子类化的NSOperation,然后将其加入NSOperationQueue。2.在主线程执行代码,方法是performSelectorOnMainThread。
3.如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone。
4、GCD的队列(dispatch_queue_t)分那两种类型?
- 串行队列Serial Dispatch Queue
- 并行队列Concurrent Dispatch Queue
3、NSOperation相比于Grand Central Dispatch有哪些优势?
iOS有四种多线程编程的技术,分别是:NSThread,Cocoa NSOperation,GCD(全称:Grand Central Dispatch),pthread。
四种方式的优缺点介绍:
1)NSThread
优点:NSThread 比其他两个轻量级。
缺点:需要自己管理线程的生命周期,线程同步。
线程同步对数据的加锁会有一定的系统开销。2)NSOperation
优点:不需要关心线程管理, 数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation相关的类是NSOperation, NSOperationQueue.NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类: NSInvocationOperation和NSBlockOperation.创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。3)GCD
(全优点)Grand Central dispatch(GCD)是Apple开发的一个多核编程的解决方案。
在iOS4.0开始之后才能使用。
GCD是一个替代NSThread, NSOperationQueue,NSInvocationOperation等技术的很高效强大的技术。4)pthread
是一套通用的多线程API,适用于Linux\Windows\Unix,跨平台,可移植,使用C语言,生命周期需要程序员管理,IOS开发中使用很少。GCD Queue 分为三种:
1,The main queue :主队列,主线程就是在个队列中。
2,Global queues : 全局并发队列。
3,用户队列:是用函数 dispatch_queue_create创建的自定义队列dispatch_sync 和 dispatch_async 区别:
1.dispatch_async(queue,block) async 异步队列,dispatch_async函数会立即返回, block会在后台异步执行。
2.dispatch_sync(queue,block) sync 同步队列,dispatch_sync函数不会立即返回,及阻塞当前线程,等待 block同步执行完成。GCD线程死锁
GCD 确实好用 ,很强大,相比NSOpretion 无法提供 取消任务的功能。
如此强大的工具用不好可能会出现线程死锁。五个案例让你明白GCD死锁
http://ios.jobbole.com/82622/
61.线程间怎么通信?
(1) GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
//下载图片
UIImage *image = nil;
dispatch_async(dispatch_get_main_queue(),^{
//回到主线程
});
}
(2) NSThread的线程通信
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
//下载图片
UIImage *image = nil;
[selfperformSelector:@selector(settingImage:) onThread:[NSThread mainThread]withObject:image waitUntilDone:YES modes:nil];
}
这种情况也适用于子线程之间的通信。
7、同步异步、串行和并行
串行与并行针对的是队列,而同步与异步,针对的则是线程。最大的区别在于,同步线程要阻塞当前线程,必须要等待同步线程中的任务执行完,返回以后,才能继续执行下一任务;而异步线程则是不用等待。
1.多线程的底层实现?
1>首先搞清楚什么是线程、什么是多线程
2> Mach是第一个以多线程方式处理任务的系统,因此多线程的底层实现机制是基于Mach的线程
3>开发中很少用Mach级的线程,因为Mach级的线程没有提供多线程的基本特征,线程之间是独立的
4>开发中实现多线程的方案C语言的POSIX接口:#include
OC的NSThread
C语言的GCD接口(性能最好,代码更精简)
OC的NSOperation和NSOperationQueue(基于GCD)
2.线程间怎么通信?
1> performSelector:onThread:withObject:waitUntilDone:
2> NSMachPort
(基本机制:A线程(父线程)创建NSMachPort对象,并加入A线程的run loop。当创建B线程(辅助线程)时,将创建的NSMachPort对象传递到主体入口点,B线程(辅助线程)就可以使用相同的端口对象将消息传回A线程(父线程)。
http://mobile.51cto.com/hot-403083_all.htm)
3.网络图片处理问题中怎么解决一个相同的网络地址重复请求的问题?
利用字典(图片地址为key,下载操作为value)
4.用NSOpertion和NSOpertionQueue处理A,B,C三个线程,要求执行完A,B后才能执行C,怎么做?
//创建队列
NSOperationQueue *queue =[[NSOperationQueue alloc] init];
//创建3个操作
NSOperation *a = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@”operation1---“);
}];
NSOperation *b= [NSBlockOperation blockOperationWithBlock:^{
NSLog(@”operation1---“);
}];
NSOperation *c= [NSBlockOperation blockOperationWithBlock:^{
NSLog(@”operation1---“);
}];
//添加依赖
[c addDependency:a];
[c addDependency:b];
//执行操作
[queue addOperation:a];
[queue addOperation:b];
[queue addOperation:c];
5.列举cocoa中常见对几种多线程的实现,并谈谈多线程安全的几种解决办法及多线程安全怎么控制?
1>只在主线程刷新访问UI
2>如果要防止资源抢夺,得用synchronized进行加锁保护
3>如果异步操作要保证线程安全等问题,尽量使用GCD(有些函数默认就是安全的)
6.GCD内部怎么实现的
1> iOS和OS X的核心是XNU内核,GCD是基于XNU内核实现的
2> GCD的API全部在libdispatch库中
3> GCD的底层实现主要有Dispatch Queue和Dispatch Source
Dispatch Queue:管理block(操作)
Dispatch Source:处理事件(MACH端口发送,MACH端口接收,检测与进程相关事件等10种事件)
7.你用过NSOperationQueue么?如果用过或者了解的话,你为什么要使用NSOperationQueue,实现了什么?请描述它和GCD的区别和类似的地方(提示:可以从两者的实现机制和适用范围来描述)。
1> GCD是纯C语言的API,NSOperationQueue是基于GCD的OC版本封装
2> GCD只支持FIFO(先进先出)的队列,NSOperationQueue可以很方便地调整执行顺序、设置最大并发数量
3> NSOperationQueue可以在轻松在Operation间设置依赖关系,而GCD需要写很多的代码才能实现
4> NSOperationQueue支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)
5> GCD的执行速度比NSOperationQueue快任务之间不太互相依赖:GCD
任务之间有依赖\或者要监听任务的执行情况:NSOperationQueue
8.既然提到GCD,那么问一下在使用GCD以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?
Block的使用注意:
1>block的内存管理
2>防止循环retian非ARC(MRC):__block
ARC:__weak__unsafe_unretained
9.在异步线程中下载很多图片,如果失败了,该如何处理?请结合RunLoop来谈谈解决方案.(提示:在异步线程中启动一个RunLoop重新发送网络请求,下载图片)
1>重新下载图片
2>下载完毕,利用RunLoop的输入源回到主线程刷新UIImageVIUew
2、多线程?
一个进程开启多条线程,每条线程并发地执行不同的任务。多线程的并发(同时)执行,其实是CPU快速地在多个线程间调度(切换)
优点:可以适当的提高程序的执行效率,能适当提高资源利用率(CPU、内存利用率),
缺点:开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
线程越多,CPU在调度线程上的开销就越大
程序设计更加复杂:比如线程之间的通信、多线程的数据共享
一般开1~3条线程,不能超过5条
主线程:(UI线程)处理UI刷新、处理事件、用户交互
子线程:(异步线程、后台线程):处理比较耗时的操作(如下载等)
3、创建多线程的4种方式?
pthread、NSThread、NSOperation、GCD
区别:
a、pthread:POSIX线程,是基于C语言的,调用的时候需要引用头文件
#import ,需要手动的管理线程的生命周期,使用起来比较麻烦
b、NSThread是苹果封装后的基于OC的,是面向对象的,但是还是需要手动管理线程的生命周期
c、GCD:Grand Central Dispatch,是基于C语言底层的API,是基于XNU内核的,会充分利用cpu的内核,而且无需我们手动管理线程的生命周期(线程的创建、调度、销毁等操作),GCD的API全部在libdispatch库中
GCD的底层实现主要有Dispatch Queue和Dispatch Source
Dispatch Queue:管理block(操作)
Dispatch Source:处理事件
GCD的核心思想是将操作放入队列中执行:操作放在block中,队列是queue
GCD的操作有两种:
并发队列:全局并发队列,也可以手动创建
串行队列:主队列,也可以手动创建
串行队列:
dispatch_queue_t queue =dispatch_queue_create("tk.bourne.testQueue",NULL);
dispatch_queue_t queue =dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
并行队列
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue",DISPATCH_QUEUE_CONCURRENT);
第二个参数用来表示创建的队列是串行的还是并行的,传入DISPATCH_QUEUE_SERIAL或NULL表示创建串行队列。传入DISPATCH_QUEUE_CONCURRENT表示创建并行队列。
d、NSOperation:是苹果基于GCD的oc版本的封装,面向对象的,自动管理线程的声明周期,可以很方便的设置线程间的依赖,调整线程的执行顺序,设置最大并发数,支持kvo,可以检测线程的执行状态,是否正在执行、是否结束,是否取消。
任务:NSOperation的派生类:NSInvocationOperation和NSBlockOperation
创建一个任务,执行start方法启动任务,调用cancel取消任务
队列:主队列和其他队列
NSOperationQueue
*queue = [NSOperationQueue mainQueue];主队列
*queue = [[NSOperationQueue alloc]init];其他队列
放入主队列的任务在主线程中按顺序一个接一个的执行
放入其他队列的任务会在其他线程并发执行
注:在其他队列中,通过设置最大并发数,控制同步还是异步,如设置最大并发量为1,就是同步执行,否则为异步执行
4、什么是线程同步?
多个线程抢夺同一块资源,造成数据安全问题,例如买票
解决方案:互斥锁、同步锁、同步操作、信号量、以及其他加锁方式(条件锁,递归锁等)
1、@synchronized(锁对象,一般直接写:self){
//需要执行的代码
},(最简单的方法)
@synchronized(self) {
//需要执行的代码块
}
NSLock : lock加锁,unlock解锁,tryLock尝试获得锁对象
2、同步执行,GCD的串行队列和NSOperationQueue其他队列
设置queue的maxConcurrentOperationCount为1
3、GCD中信号量dispatch_semaphore_t,也可以防止资源抢夺
4、NSCondition控制线程通信,类似GCD的信号量机制,也可以解决资源抢夺问题,NSCondition更重要的是解决线程之间的调度关系
5、线程间通信的方法
performSelect : onThread : withObject : waitUntilDone
performSelectOnMainThread : withObject : waitUntilDone//跟主线程通信
dispatch_async(dispatch_get_main_queue(),^{ }); //GCD回主线程
[[NSOperationQueue mainQueue]addOperationWithBlock:^{ }];//跟主线程通信
6、线程死锁?
经典的死锁现象是GCD的同步操作串行队列:
NSLog(@"1"); //任务1
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"2");
//任务2死锁
});
NSLog(@"3"); //任务3
只会打印:1
进程死锁:
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
产生原因:资源竞争及进程推进顺序非法
死锁的4个必要条件:互斥、请求保持、不可剥夺、环路
死锁的处理:鸵鸟策略、预防策略、避免策略、检测与解除死锁
19、项目中哪块用到多线程,碰到了什么问题,怎么解决的
什么时候使用多线程:
将耗时、查询或者并发需求高等任务分配到其他线程执行,并由主线程负责统一更新界面会使得应用程序更加流畅,用户体验更好,例如网络请求,播放游戏的背景音乐,文件下载等。
碰到的问题以及解决办法:
(1)使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢。
使用以下方法可以控制并发的线程数量:
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
使用addDependency可以建立操作之间的依赖关系,设定操作的执行顺序
(2)当多个线程对同一个资源出现争夺的时候需要注意线程安全问题:
添加互斥锁。
(3)更新UI界面,处理界面和用户之间的交互事件一定要在主线程中处理。多线程中栈与堆是公有的还是私有的?
一般来说栈是私有的,堆是公有的;但是可以为特定的线程创建私有的堆。
在多线程环境下,每个线程拥有一个栈和一个程序计数器。栈和程序计数器用来保存线程的执行历史和线程的执行状态,是线程私有的资源。其他的资源(比如堆、地址空间、全局变量)是由同一个进程内的多个线程共享。堆: 是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
栈:是个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是线程安全的。操作系统在切换线程的时候会自动的切换栈,就是切换 SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
21、GCD的队列(dispatch_queue_t)分哪两种类型?编写代码:使⽤用并⾏行队列实现多线程开发
串⾏行队列Serial Dispatch Queue
并⾏行队列Concurrent Dispatch Queue
1:dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIO RITY_DEFAULT, 0);
2:dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIO RITY_HIGH, 0);
3:dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIO RITY_LOW, 0);
5、列举几种进程的同步机制并进行比较。
(1)信号量机制:一个信号量只能置一次初值,以后只能对之进行p操作或v操作。由此也可以看到,信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点。信号量机制功能强大,但使用时对信号量的操作分散, 而且难以控制,读写和维护都很困难。加重了程序员的编码负担;核心操作P-V分散在各用户程序的代码中,不易控制和管理;一旦错误,后果严重,且不易发现和纠正。
(2)自旋锁:自旋锁是为了保护共享资源提出的一种锁机制。调用者申请的资源如果被占用,即自旋锁被已经被别的执行单元保持,则调用者一直循环在那里看是否该自旋锁的保持着已经释放了锁。自旋锁是一种比较低级的保护数据结构和代码片段的原始方式,可能会引起以下两个问题:
1、死锁;
2、过多地占用CPU资源传统自旋锁由于无序竞争会导致“公平性”问题。(3)管程:信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。
(4)会合:进程直接进行相互作用
(5)分布式系统:由于在分布式操作系统中没有公共内存,因此参数全为值参,而且不可为指针。
6、导致进程死锁的原因是什么,如何解决。
产生死锁的原因:
一是系统提供的资源数量有限,不能满足每个进程的使用;
二是多道程序运行时,进程推进顺序不合理。产生死锁的必要条件是:
1、互斥条件;
2、不可剥夺条件(不可抢占);
3、部分分配;
4、循环等待。根据产生死锁的四个必要条件,只要使其中之一不能成立,死锁就不会出现。为此,可以采取下列三种预防措施:
1、采用资源静态分配策略,破坏"部分分配"条件;
2、允许进程剥夺使用其他进程占有的资源,从而破坏"不可剥夺"条件;
3、采用资源有序分配法,破坏"环路"条件。死锁的避免不严格地限制死锁的必要条件的存在,而是系统在系统运行过程中小心地避免死锁的最终发生。最著名的死锁避免算法是银行家算法。死锁避免算法需要很大的系统开销。
解决死锁的另一条途径是死锁检测方法,这种方法对资源的分配不加限制,即允许死锁的发生。但系统定时地运行一个"死锁检测"程序,判断系统是否已发生死锁,若检测到死锁发生则设法加以解除。
解除死锁常常采用下面两种方法:1、资源剥夺法;2、撤消进程法
解决策略:鸵鸟策略、预防策略、避免策略、检测与解除死锁
7.线程注意事项
关于多线程的详细介绍
http://mobile.51cto.com/iphone-403490.htm1、线程的堆栈大小
iPhone设备上的应用程序开发也是属于嵌入式设备的开发,同样需要注意嵌入式设备开发时的几点问题,比如资源上限,处理器速度等。
iPhone中的线程应用并不是无节制的,官方给出的资料显示iPhone OS下的主线程的堆栈大小是1M,第二个线程开始都是512KB。并且该值不能通过编译器开关或线程API函数来更改。2、Autorelease
如果你什么都不考虑,在线程函数内调用autorelease,会出现错误3、子线程中描画窗口
多线程编程中普遍遵循一个原则,就是一切与UI相关的操作都由主线程做,子线程只负责事务,数据方面的处理。那么如果想在子线程中更新UI时怎么做呢?如果是在windows下,你会PostMessage一个描画更新的消息,在iPhone中,需要使用performSelectorOnMainThread委托主线程处理。总结:
多线程能适当提高程序的执行效率,能适当提高资源利用率(CPU、内存利用率),但是开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能,而且线程越多,CPU在调度线程上的开销就越大,使用多线程程序设计更加复杂:比如线程之间的通信、多线程的数据共享一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
主线程的使用注意:别将比较耗时的操作放到主线程中。耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验
19.多线程在实际项目中的应用中有没有碰到什么问题
什么时候使用多线程:
将耗时、查询或者并发需求高等任务分配到其他线程执行,并由主线程负责统一更新界面会使得应用程序更加流畅,用户体验更好,例如网络请求,播放游戏的背景音乐,文件下载等。碰到的问题以及解决办法:
(1)使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢
使用以下方法可以控制并发的线程数量:
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
使用addDependency可以建立操作之间的依赖关系,设定操作的执行顺序
(2)当多个线程对同一个资源出现争夺的时候需要注意线程安全问题
(3)更新UI界面,处理界面和用户之间的交互事件一定要在主线程中处理
dispatch_async(dispatch_get_mian_queue(),^{
//这里应该写哪些代码?
}
12、谈谈你对多线程开发的理解?ios中有几种实现多线程的方法?
好处:
①、使用线程可以把程序中占据时间长的任务放到后台去处理,如图片、视频的下载
②、发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好
缺点:
①、大量的线程降低代码的可读性,
②、更多的线程需要更多的内存空间
③、当多个线程对同一个资源出现争夺的时候要注意线程安全的问题。
iOS有三种多线程编程的技术:
①、NSThread(两种创建方式)
[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil];
[myThread start];
②、NSOperationQueue
NSOperationQueue *oprationQueue = [[NSOperationQueue alloc] init];
oprationQueue addOperationWithBlock:^{
//这个block语句块在子线程中执行
}
http://alloc.[sinaapp.com/wp/?p=237](http://sinaapp.com/wp/?p=237)
③、Grand Central Dispatch (GCD)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//耗时的操作
dispatch_async(dispatch_get_main_queue(), ^{
//更新界面
});
});
http://blog.[csdn.net/totogo2010/article/details/8016129](http://csdn.net/totogo2010/article/details/8016129)
PS:不显示的创建线程的方法:
用NSObject的类方法performSelectorInBackground:withObject:
创建一个线程:
[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];
13、线程同步和异步的区别?IOS中如何实现多线程的同步?
同步:一个线程要等待上一个线程执行完之后才能执行当前的线程,生活中的例子(上厕所)。
异步:同时去做两件或者多件事。比如边听歌边看报。
14、 GCD的队列(dispatch_queue_t)分哪两种类型?
串行队列Serial Dispatch Queue
并行队列Concurrent Dispatch Queue
15、你参与的APP,是如何处理多个服务的同步发起的?
使用iOS线程技术,创建并行队列来分别执行不同的任务。将不同的任务加入到子队列当中,任务执行后回到主线程当中刷新UI界面。
APP界面一般都是根据产品需求和UI效果图来完成的,但是我们为了提高代码的复用性,会将视图的实现进行封装,然后在控制器当中进行加载调用。
16、NSOperationQueue有哪些使用方式?
一种在iOS中执行并发操作的方法,是使用NSOperation和NSOperationQueue类。
另一种处理操作之间的依赖关系,如果操作直接有依赖关系,比如第二个操作必须等第一个操作结束后再执行。控制线程池中的线程数,具体参考http://blog.sina.com.cn/s/blog_45e2b66c0100ztz7.html
17、NSThread中的Runloop的作用,如何使用?
有些情况下,我们还是在运行一些长线任务或者复杂任务的时候需要用比较原始的NSThread。这就需要为NSThread创建一个run loop.
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(playerThread: ) object:nil];
[thread start];
//如果要利用NSOperation,原理类似。只需要加入到queue里面去就好了。queue会在合适的时机调用方法,下面代码作为参考。
- (void) playerThread: (void*)unused
{
audioRunLoop = CFRunLoopGetCurrent();//子线程的runloop引用
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];//子线程的
[self initPlayer]; CFRunLoopRun(); //运行子线程的
[pool release]; // run loop,这里就会停住了。
}
//实现一个timer,用于检查子线程的工作状态,并在合适的时候做任务切换。或者是合适的时候停掉自己的
-(void) initPlayer
{
//在这里你可以初始化一个工作类,比如声音或者视频播放
NSTimer *stateChange = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(checkStatuserInfo:nil repeats:YES];
}
-(void) checkState:(NSTimer*) timer
{
if(需要退出自线程了) {
//释放子线程里面的资源
CFRunLoopStop( CFRunLoopGetCurrent());//结束子线程任务
}
}
18、NSThread、NSOperationQueue以及GCD等多线程编程技术什么情况下使用?
1)NSThread
优点:NSThread比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销2)Cocoa NSOperation
优点:不需要关心线程管理, 数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation相关的类是NSOperation, NSOperationQueue.
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类: NSInvocationOperation和NSBlockOperation.
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。3)GCD使用过程中遇到的问题?
3.NSOperationQueue中有一个属性叫maxConcurrentCount即最大并发数。这里所谓的最大并发数指的是什么含义?
答:这里的最大并发数指的是在队列中同时执行的任务的个数。有很多人会认为是所分配的线程的个数。其实不是的。因为线程的个数的多少取决于系统,系统会分配合适的线程数量来保证这些任务并发执行的,作为程序员我们是没办法控制的。
4.在项目中什么时候选择使用GCD,什么时候选择使用NSOperation?
答:无论是GCD还是NSOperation其实都是多线程的一种实现形式。严格的说NSOperation和线程并没有必然联系,更不是多线程,NSOperation只是操作,封装了target和action或者Block,在主线程中执行Operation,Operation就会运行在主线程中,在子线程中执行Operation,Operation就运行在子线程中。它只有和NSOperationQueue联合使用的时候,才能发挥出价值。NSOperationQueue是Operation的管理者,它首先是一个队列,先来的任务先开始执行,后来的任务后开始执行,这些任务的执行是并发的,NSOperationQueue负责为任务开辟线程以及关闭线程,有点类似于cell重用,用有限的线程执行任意数量的任务,同时可以设置最大并发数,控制程序的性能,不至于全部任务一起执行。GCD出现比NSOperationQueue要晚,是一套基于C函数的API,由于是C函数,所以性能比较高,使用灵活,安全,当然功能也强大。但是相对于GCD来讲,NSOperationQueue对线程做了封装,使用比较简单,尤其是不用管理线程的开启和关闭。个人认为,如果仅仅是想要实现异步执行任务,首选GCD,如果要管理多个异步任务,NSoperationQueue比较方便。如果要做更复杂的处理,比如前5个任务并发,5个任务执行完之后,需要串行执行3个任务,之后再并发执行若干任务,就需要使用GCD了。总的来说,特别简单的和非常复杂的多线程任务GCD比较适合,介于二者之间的用NSOperationQueue比较合适。
5.写出在多线程情况下的一个单例。
答:一般情况下,在开发中我们所写的单例都是伪单例。即只是保证了在调用某一方法时,所产生的对象只有一个,但是没有考虑其他的影响其引用计数的方法。例如retain、copy等。为了保证线程安全,单例的写法如下所示:
方法一:
static AccountManager *sharedAccountManagerInstance = nil;
+ (AccountManager *)sharedManager{
@synchronized (self){
if (sharedAccountManagerInstance == nil) {
sharedAccountManagerInstance = [[AccountManager alloc] init];
}
}
return sharedAccountManagerInstance;
}
方法二:
static AccountManager *sharedAccountManagerInstance = nil;
+ (AccountManager *)sharedManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedAccountManagerInstance = [[AccountManager alloc] init];
});
return sharedAccountManagerInstance;
}
6、NSThread中的Runloop的作用,如何使用?
每个线程(NSThread)对象中内部都有一个run loop(NSRunLoop)对象用来循环处理输入事件,处理的事件包括两类,一是来自Input sources的异步事件,一是来自Timer sources的同步事件;
run Loop在处理输入事件时会产生通知,可以通过Core Foundation向线程中添加run-loop observers来监听特定事件,以在监听的事件发生时做附加的处理工作。 主线程的Run Loop会在App运行时自动运行,子线程中需要手动运行。
Run Loop就是一个处理事件源的循环,你可以控制这个Run Loop运行多久,如果当前没有事件发生,Run Loop会让这个线程进入睡眠状态(避免再浪费CPU时间),如果有事件发生,Run Loop就处理这个事件。
如果子线程进入一个循环需要不断处理一些事件,那么设置一个Run Loop是最好的处理方式,如果需要Timer,那么Run Loop就是必须的。
开发中遇到的需要使用Run Loop的情况有:
需要使用Port或者自定义Input Source与其他线程进行通讯。
子线程中使用了定时器;
使用任何performSelector*****到子线程中运行方法
使用子线程去执行周期性任务
NSURLConnection在子线程中发起异步请求
20、描述runloop和线程的关系
Run loop,正如其名,loop表⽰示某种循环,和run放在一起就表⽰一直在运⾏行着的循环。实际上,run loop和线程是紧密相连的,可以这样说run loop是为了线程⽽生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分, Cocoa和CoreFundation都提供了run loop对象⽅方便配置和管理线程的run loop(以下都已Cocoa为例)。每个线程,包括程序的主线程(main thread)都有与之相 应的run loop对象。
14、runloop和线程有什么关系?
总的说来,Run loop,正如其名,loop表示某种循环,和run放在一起就表示一直在运行着的循环。实际上,run loop和线程是紧密相连的,可以这样说run loop是为了线程而生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分,Cocoa和CoreFundation都提供了run loop对象方便配置和管理线程的run loop(以下都以Cocoa为例)。每个线程,包括程序的主线程(main thread)都有与之相应的run loop对象。
runloop和线程的关系:主线程的run loop默认是启动的。iOS的应用程序里面,程序启动后会有一个如下的main()函数
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
重点是UIApplicationMain()函数,这个方法会为main thread设置一个NSRunLoop对象,这就解释了:为什么我们的应用可以在无人操作的时候休息,需要让它干活的时候又能立马响应。
对其它线程来说,run loop默认是没有启动的,如果你需要更多的线程交互则可以手动配置和启动,如果线程只是去执行一个长时间的已确定的任务则不需要。
在任何一个Cocoa程序的线程中,都可以通过以下代码来获取到当前线程的run loop。
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
6、线程间通讯
1、在后台线程下载图像
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
2、在主线程设置图像
// waitUntilDone:是否等待主线程执行完毕 setImage:方法。
// YES:等待 NO:不等待
// 一般不用等待,直接设置 NO 即可
[self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
13、线程间通讯
主线程实现
1、定义属性
/// 根视图是滚动视图
@property (nonatomic, strong) UIScrollView *scrollView;
/// 图像视图
@property (nonatomic, weak) UIImageView *imageView;
/// 网络下载的图像
@property (nonatomic, weak) UIImage *image;
2、loadView 方法
加载视图层次结构
用纯代码开发应用程序时使用
功能和 Storyboard & XIB 是等价的
- (void)loadView {
_scrollView = [[UIScrollView alloc] init];
_scrollView.backgroundColor = [UIColor orangeColor];
self.view = _scrollView;
UIImageView *iv = [[UIImageView alloc] init];
[self.view addSubview:iv];
_imageView = iv;
}
3、viewDidLoad 方法
视图加载完成后执行
可以做一些数据初始化的工作
如果用纯代码开发,不要在此方法中设置界面 UI
- (void)viewDidLoad {
[super viewDidLoad];
// 下载图像
[self downloadImage];
}
4、下载网络图片
- (void)downloadImage{
// 1. 网络图片资源路径
NSURL *url = [NSURL URLWithString:@"[http://c.hiphotos.baidu.com/image/pic/item/4afbfbedab64034f42b14da1aec379310a551d1c.jpg](http://c.hiphotos.baidu.com/image/pic/item/4afbfbedab64034f42b14da1aec379310a551d1c.jpg)"];
// 2. 从网络资源路径实例化二进制数据(网络访问)
NSData *data = [NSData dataWithContentsOfURL:url];
// 3. 将二进制数据转换成图像
UIImage *image = [UIImage imageWithData:data];
// 4. 设置图像
self.image = image;
}
5、设置图片
- (void)setImage:(UIImage *)image {
// 1. 设置图像视图的图像
self.imageView.image = image;
// 2. 按照图像大小设置图像视图的大小
[self.imageView sizeToFit];
// 3. 设置滚动视图的 contentSize
self.scrollView.contentSize = image.size;
}
6、设置滚动视图的缩放
1)设置滚动视图缩放属性
// 1> 最小缩放比例
self.scrollView.minimumZoomScale = 0.5;
// 2> 最大缩放比例
self.scrollView.maximumZoomScale = 2.0;
// 3> 设置代理
self.scrollView.delegate = self;
2)实现代理方法 - 告诉滚动视图缩放哪一个视图
#pragma mark - UIScrollViewDelegate 代理方法
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
7、线程间通讯
1)在后台线程下载图像
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
2)在主线程设置图像
// waitUntilDone:是否等待主线程执行完毕 setImage:方法。
// YES:等待 NO:不等待
// 一般不用等待,直接设置 NO 即可
[self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
3.多线程的实现,你用过哪些?如何保证线程安全?
1.使用互斥锁
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源2.原子和非原子属性
OC在定义属性时有nonatomic和atomic两种选择
atomic:原子属性,为setter方法加锁(默认就是atomic)
nonatomic:非原子属性,不会为setter方法加锁atomic:线程安全,需要消耗大量的资源
nonatomic:非线程安全,适合内存小的移动设备
*96.关于下面线程管理错误的是(B)
//不确定
A.GCD在后端管理着一个线程池
B.NSOperationQueue是对NSthread的更高层的封装
C.NSThread需要自己管理线程的生命周期
D.GCD可以根据不同优先级分配线程,对
102.请使用gcd完成如下任务,执行并发任务task1,task1完成后update UI.
答:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
//task1:
NSLog(@"执行task1");
//更新UI
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"更新UI");
});
});
103.为什么在主线程中更新UI?子线程中想要更新UI怎么做?
答:
(1)在子线程中不能更新UI,除了极少数UI外,其他UI更新要等到子线程执行完毕后回到主线程中进行更新。如果子线程一直在运行,则子线程中UI更新的函数栈主线程无法得知,即UI无法更新;
(2)回到主线程中进行UI更新;
7、Object_C中创建线程的方法有哪些?如果在主线程中执行代码,方法是什么?如果想在延时执行代码,方法又是什么?
创建线程方式 GCD,NSOpertaion,NSThread
主线程要用 dispatch_get_main_queue()
延时的方法dispatch_after
- Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码,方法又是什么?
线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;
在主线程执行代码,方法是performSelectorOnMainThread,
如果想延时执行代码可以用
performSelector:onThread:withObject: afterDelay:
或者使用GCD的函数:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后异步执行这里的代码...
});
3.列举Cocoa中常用的几种多线程实现,并谈谈多线程安全问题的几种解决方案,什么地方会用到多线程?
多线程实现方法:
1、Thread;
2、Cocoa operations;
3、Grand Central Dispatch (GCD) (iOS4 才开始支持)多线程安全问题解决方案:
添加互斥锁,
1、列举Cocoa中常见对几种线程的实现,并谈谈多线程安全的几种解决办法和多线程安全怎么控制.
Cocoa中常见对几种线程的实现:
(1)OC的NSThread
(2) C语言的GCD接口(性能最好,代码更精简)
(3) OC的NSOperation和NSOperationQueue(基于GCD)多线程安全的几种解决办法
(1)只在主线程刷新访问UI
(2)如果要防止资源抢夺,得用synchronize进行加锁保护。
(3)如果异步操作要保证线程安全等问题,尽量使用GCD。(GCD有些函数默认就是安全的)
9、GCD的queue,main queue中执行的代码,一定是在main thread么?
看:http://www.tuicool.com/articles/AVnMVj
1、对于queue中所执行的代码不一定在main thread中。如果queue是在主线程中创建的,那么所执行的代码就是在主线程中执行。如果是在子线程中创建的,那么就不会在main thread中执行。
2、对于main queue就是在主线程中的,因此一定会在主线程中执行。获取main queue就可以了,不需要我们创建,获取方式通过调用方法dispatch get main_queue来获取。
10、从用户体验角度举例说明同步和异步。
答:
1.同步意味着线程阻塞,在主线程中使用此方法会不响应任何用户事件。所以,在应用程序设计时,大多被用在专门的子线程增加用户体验,或用异步请求代替。
2.异步请求的好处是不阻塞当前线程,但相对于同步请求略为复杂,至少要添加两个回调方法来获取异步事件,从用户的体验来说,异步请求数据的APP比同步请求的APP操作更加流畅,快捷,
11、以下两种GCD队列创建有什么不同?
dispatch_queue_t queue = dispatch_queue_create("MyQueue”, DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue = dispatch_queue_create(@"MyQueue", DISPATCH_QUEUE_CONCURRENT);
//生成一个串行队列,队列中的block按照先进先出(FIFO)的顺序去执行,实际上为单线程执行。第一个参数是队列的名称,在调试程序时会非常有用,所有尽量不要重名了。
// 生成一个并发执行队列,block被分发到多个线程去执行
11、线程是什么?进程是什么?二者有什么区别和联系?
线程是CPU独立运行和独立调度的基本单位(可以理解为一个进程中执行的代码片段);
进程是资源分配的基本单位(进程是一块包含了某些资源的内存区域)。
进程是线程的容器,真正完成代码执行的是线程,而进程则作为线程的执行环境。
一个程序至少包含一个进程,一个进程至少包含一个线程,一个进程中的多个线程共享当前进程所拥有的资源。
8、线程和进程的区别和联系
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响。
而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
1、线程和进程?
进程:用于指代一个正在运行的可执行程序,它可以包含多个线程,是拥有独立资源的最小单元
线程:用于指代独立执行的代码段。线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位
两者之前的联系:一个进程至少包含一个线程,称为主线程,而一个线程只能属于一个进程,进程是拥有独立的地址空间,拥有独立的资源,是拥有独立资源的最小单元,线程本身不拥有系统资源,但与所在的进程中的其他线程共享进程的全部资源。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
12.线程与进程的区别与联系
进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,计算机系统资源的基本单位。每一个进程都有一个自己的地址空间,即进程空间或(虚空间)。进程空间的大小只与处理机的位数有关,一个16位长处理机的进程空间大小为216,而32位处理机的进程空间大小为232。进程至少有5种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。
线程,在网络或多用户环境下,一个服务器通常需要接收大量且不确定数量用户的并发请求,为每一个请求都创建一个进程显然是行不通的,无论是从系统资源开销方面或是响应用户请求的效率方面来看。因此,操作系统中线程的概念便被引进了。
线程,是进程的一部分,一个没有线程的进程可以被看作是单线程的。线程有时又被称为轻权进程或轻量级进程,也是CPU调度的一个基本单位。
进程
• 进程是指在系统中正在运行的一个应用程序
• 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内
• 通过 活动监视器可以查看 Mac 系统中所开启的进程线程
• 进程要想执行任务,必须得有线程,进程至少要有一条线程
• 程序启动会默认开启一条线程,这条线程被称为主线程或UI 线程
• 线程是进程的基本执行单元,进程的所有任务都在线程中执行多线程
• 一个进程中可以开启多条线程,每条线程可以同时执行不同的任务
○ 进程 -> 公司
○ 线程 -> 员工
○ 主线程 -> 老板(第一个员工)
• 多线程技术可以提高程序的执行效率多线程原理
• 同一时间,CPU只能处理一条线程,只有一条线程在执行
• 多线程同时执行,其实是CPU快速地在多条线程之间切换
• 如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
• 如果线程非常多,会在多条线程之间来回切换,消耗大量的 CPU 资源
○ 每个线程被调度的次数会降低
○ 线程的执行效率会下降iOS 8.0 主线程的默认堆栈大小也是 512K
多线程优缺点
优点
• 能适当提高资源利用率(CPU、内存利用率)
• 能适当提高程序的执行效率
缺点
• 开启线程需要占用一定的内存空间,如果开启大量的线程,会占用大量的内存空间,降低程序的性能
• 线程越多,CPU在调度线程上的开销就越大
• 程序设计更加复杂:比如线程之间的通信、多线程的数据共享主线程
• 程序启动创建的线程,被称为主线程或UI线程
• 主线程的作用
○ 显示/刷新 UI 界面
○ 处理 UI 事件:点击、滚动、拖拽等事件注意:要将耗时操作放在后台线程执行,否则会影响 UI 的流畅度,破坏用户体验
所有网络访问都是耗时操作!
173.请描述一下线程的生命周期。
新建(new Thread)、就绪(runnable)、运行(running)、死亡(dead)、堵塞(blocked)
1.NSOPrationQueue 与GCD分别在什么情况下更合适使用?
1、GCD是底层的C语言构成的API,而NSOperationQueue是基于GCD的OC封装。
2、GCD支持FIFO队列,NSOperationQueue可以方便设置执行顺序,设置最大的并发数量。
3、NSOperationQueue可以方便地设置operation之间的依赖关系,GCDF则需要更多的代码。
4、NSOperationQueue支持KVO,可以检测operation是否正在执行(isExecuted),是否结束(isFinished),是否取消(isCanceled)。
5、GCD的执行速度比NSOperationQueue快。使用场合:
任务之间不太相互依赖:GCD;
任务之间依赖或者监听任务的执行情况:NSOperationQueue那这两者直接有什么区别呢?
1、GCD是底层的C语言构成的API,而NSOperationQueue及相关对象是Objc的对象。在GCD中,在队列中执行的是由block构成的任务,这是一个轻量级的数据结构;而Operation作为一个对象,为我们提供了更多的选择;
2、在NSOperationQueue中,我们可以随时取消已经设定要准备执行的任务(当然,已经开始的任务就无法阻止了),而GCD没法停止已经加入queue的block(其实是有的,但需要许多复杂的代码);
3、NSOperation能够方便地设置依赖关系,我们可以让一个Operation依赖于另一个Operation,这样的话尽管两个Operation处于同一个并行队列中,但前者会直到后者执行完毕后再执行;
4、我们能将KVO应用在NSOperation中,可以监听一个Operation是否完成或取消,这样子能比GCD更加有效地掌控我们执行的后台任务;
5、在NSOperation中,我们能够设置NSOperation的priority优先级,能够使同一个并行队列中的任务区分先后地执行,而在GCD中,我们只能区分不同任务队列的优先级,如果要区分block任务的优先级,也需要大量的复杂代码;
6、我们能够对NSOperation进行继承,在这之上添加成员变量与成员方法,提高整个代码的复用度,这比简单地将block任务排入执行队列更有自由度,能够在其之上添加更多自定制的功能。总的来说,Operation queue 提供了更多你在编写多线程程序时需要的功能,并隐藏了许多线程调度,线程取消与线程优先级的复杂代码,为我们提供简单的API入口。从编程原则来说,一般我们需要尽可能的使用高等级、封装完美的API,在必须时才使用底层API。但是我认为当我们的需求能够以更简单的底层代码完成的时候,简洁的GCD或许是个更好的选择,而Operation queue 为我们提供能更多的选择。
135.实现多线程有哪些方法,分别有什么区别?
答: (http://www.[cnblogs.com/hanjun/p/3667874.html](http://cnblogs.com/hanjun/p/3667874.html))
1.NSThread
2.NSOperationQueue
3.GCD
区别:
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上提升效率。
34.你的项目什么时候选择使用GCD,什么时候选择NSOperation?
答:
项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
9.在项目什么时候选择使用GCD,什么时候选择NSOperation?
gcd是基于c的底层api,NSOperation属于object-c类。
相对于gcd:
1,NSOperation拥有更多的函数可用,具体查看api。
2,在NSOperationQueue中,可以建立各个NSOperation之间的依赖关系。
3,有kvo,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)。
4,NSOperationQueue可以方便的管理并发、NSOperation之间的优先级。
gcd主要与block结合使用。
项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用。
2.dispatch_queue_t有哪几种类型?
queue mainqueue globalqueue
5、使用GCD实现一个单例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});