1.多线程的优点和缺点分别是什么?
答:优点:1、将耗时较长的操作(网络请求、图片下载、音频下载、数据库访问等)放在子线程中执行,可以防止主线程的卡死;2、可以发挥多核处理的优势,提升cpu的使用率。
缺点:1、每开辟一个子线程就消耗一定的资源;2、会造成代码的可读性变差;3、如果出现多个线程同时访问一个资源,会出现资源争夺的情况
3. 2.NSOperationQueue中有一个属性叫maxConcurrentCount即最大并发数。这里所谓的最大并发数指的是什么含义?
答:这里的最大并发数指得是在队列中同时执行的任务的个数。有很多人会认为是所分配的线程的个数。其实不是的。因为线程的个数的多少取决于系统,系统会分配合适的线程数量来保证这些任务并发执行的,作为程序员我们是没办法控制的
4、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与其他线程进行通讯。
子线程中使用了定时器
使用任何perform mSelector*****到子线程中运行方法
使用子线程去执行周期性任务
NSURLConnection在子线程中发起异步请求
10)OC中创建线程的方法是什么?如果在主线程中执行代码,方法是什么
线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:
11)线程和进程的区别和联系
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程
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参数的使用,会是代码更为易读,建议在简单项目中使用。
1、线程的堆栈大小
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的流畅度,给用户一种“卡”的坏体验
25. 线程是什么?进程是什么?二者有什么区别和联系? (UI第二十二讲 多线程编程)
线程是CPU独立运行和独立调度的基本单位(可以理解为一个进程中执行的代码片段),进程是资源分配的基本单位(进程是一块包含了某些资源的内存区域)。进程是线程的容器,真正完成代码执行的是线程,而进程则作为线程的执行环境。一个程序至少包含一个进程,一个进程至少包含一个线程,一个进程中的多个线程共享当前进程所拥有的资源。
26.谈谈你对多线程开发的理解?ios中有几种实现多线程的方法?(UI第二十二讲 多线程编程)
好处:
①、使用线程可以把程序中占据时间长的任务放到后台去处理,如图片、视频的下载
②、发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好
缺点:
①、大量的线程降低代码的可读性,
②、更多的线程需要更多的内存空间
③、当多个线程对同一个资源出现争夺的时候要注意线程安全的问题。
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语句块在子线程中执行
}
③、Grand Central Dispatch (GCD)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 耗时的操作
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});
});
PS:不显示的创建线程的方法:
用NSObject的类方法 performSelectorInBackground:withObject:
创建一个线程:
[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];
27.线程同步和异步的区别?IOS中如何实现多线程的同步?
同步:一个线程要等待上一个线程执行完之后才能执行当前的线程,生活中的例子(上厕所)。
异步:同时去做两件或者多件事。比如边听歌边看报。
.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];
51. GCD的队列(dispatch_queue_t)分哪两种类型?
串行队列Serial Dispatch Queue
并行队列Concurrent Dispatch Queue
77. 你参与的APP,是如何处理多个服务的同步发起的?
使用iOS线程技术,创建并行队列来分别执行不同的任务。将不同的任务加入到子队列当中,任务执行后回到主线程当中刷新UI界面。
APP 界面一般都是根据产品需求和UI效果图来完成的,但是我们为了提高代码的复用性,会将视图的实现进行封装,然后在控制器当中进行加载用
88.NSOperationQueue有哪些使用方式?
一种在iOS中执行并发操作的方法,是使用NSOperation和NSOperationQueue类。在本教程中,你将学习如何使用它们!你会先创建一款不使用多线程的app,这样它会变得响应非常迟钝。然后改进程序,添加上并行操作–并且希望–可以提供一个交互响应更好的界面给用户!
另一种处理操作之间的依赖关系,如果操作直接有依赖关系,比如第二个操作必须等第一个操作结束后再执行。控制线程池中的线程数
89.NSThread中的Runloop的作用,如何使用?
有些情况下,我们还是在运行一些长线任务或者复杂任务的时候需要用比较原始的NSThread。这就需要为NSThread创建一个run loop.
1 NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(playerThread: ) object:nil];
2 [thread start];
3 //如果要利用NSOperation,原理类似。只需要加入到queue里面去就好了。。queue会在合适的时机调用方法,下面代码作为参考。
4 - (void) playerThread: (void*)unused
5 {
6 audioRunLoop = CFRunLoopGetCurrent();//子线程的runloop引用
7 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];//子线程的
8 [self initPlayer]; CFRunLoopRun(); //运行子线程的
9 [pool release]; // run loop,这里就会停住了。
10 }
11 // 实现一个timer,用于检查子线程的工作状态,并在合适的时候做任务切换。或者是合适的时候停掉自己的
12 -(void) initPlayer {
13 // 在这里你可以初始化一个工作类,比如声音或者视频播放
14 NSTimer *stateChange = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:
15 @selector(checkStatuserInfo:nil repeats:YES];
16 }
17 -(void) checkState:(NSTimer*) timer
18 {
19 if(需要退出自线程了) {
20 //释放子线程里面的资源
21 CFRunLoopStop( CFRunLoopGetCurrent());//结束子线程任务
22 }
23 }
3. NSThread、NSOperationQueue以及GCD等多线程编程技术什么情况下使用?
1)NSThread
优点:NSThread比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
2)Cocoa NSOperation
优点:不需要关心线程管理, 数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation相关的类是NSOperation, NSOperationQueue.
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类: NSInvocationOperation和NSBlockOperation.
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。
2)GCD使用过程中遇到的问题?
44.项目中哪块用到多线程,碰到了什么问题,怎么解决的
什么时候使用多线程:
将耗时、轮询或者并发需求高等任务分配到其他线程执行,并由主线程负责统一更新界面会使得应用程序更加流畅,用户体验更好,例如网络请求,播放游戏的背景音乐,文件下载等。
碰到的问题以及解决办法:
(1)使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢
使用以下方法可以控制并发的线程数量:
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
使用addDependency可以建立操作之间的依赖关系,设定操作的执行顺序
(2)当多个线程对同一个资源出现争夺的时候需要注意线程安全问题
(3)更新UI界面,处理界面和用户之间的交互事件一定要在主线程中处理。
‘多线程中栈与堆是公有的还是私有的
一般来说栈是私有的,堆是公有的;但是可以为特定的线程创建私有的堆
在多线程环境下,每个线程拥有一个栈和一个程序计数器。栈和程序计数器用来保存线程的执行历史和线程的执行状态,是线程私有的资源。其他的资源(比如堆、地址空间、全局变量)是由同一个进程内的多个线程共享。
堆: 是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
栈:是个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是 thread safe的。操作系统在切换线程的时候会自动的切换栈,就是切换 SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
24、描述runloop和线程的关系
Run loop,正如其名,loop表⽰示某种循环,和run放在⼀一起就表⽰示⼀一直在运⾏行着 的循环。实际上,run loop和线程是紧密相连的,可以这样说run loop是为了线 程⽽而⽣生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分, Cocoa和CoreFundation都提供了run loop对象⽅方便配置和管理线程的run loop(以
下都已Cocoa为例)。每个线程,包括程序的主线程(main thread)都有与之相 应的run loop对象。
8、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);
1、列举Cocoa中常见对几种线程的实现,并谈谈多线程安全的几种解决办法和多线程安全怎么控制.
Cocoa中常见对几种线程的实现:
(1)OC的NSThread
(2) C语言的GCD接口(性能最好,代码更精简)
(3) OC的NSOperation和NSOperationQueue(基于GCD)
多线程安全的几种解决办法
(1)只在主线程刷新访问UI
(2)如果要防止资源抢夺,得用synchronize进行加锁保护。
(3)如果异步操作要保证线程安全等问题,尽量使用GCD。(GCD有些函数默认就是安全的)
19.多线程在实际项目中的应用中有没有碰到什么问题什么时候使用多线程:
将耗时、轮询或者并发需求高等任务分配到其他线程执行,并由主线程负责统一更新界面会使得应用程序更加流畅,用户体验更好,例如网络请求,播放游戏的背景音乐,文件下载等。
碰到的问题以及解决办法:
(1)使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢
使用以下方法可以控制并发的线程数量:
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
使用addDependency可以建立操作之间的依赖关系,设定操作的执行顺序
(2)当多个线程对同一个资源出现争夺的时候需要注意线程安全问题
(3)更新UI界面,处理界面和用户之间的交互事件一定要在主线程中处理
3. 2.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;
}
10)OC中创建线程的方法是什么?如果在主线程中执行代码,方法是什么
线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:
11)线程和进程的区别和联系
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。 进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程
2、Autorelease
如果你什么都不考虑,在线程函数内调用 autorelease,会出现错误
3、子线程中描画窗口
多线程编程中普遍遵循一个原则,就是一切与UI相关的操作都有主线程做,子线程只负责事务,数据方面的处理。那么如果想在子线程中更新UI时怎么做呢?如果是在windows下,你会PostMessage 一个描画更新的消息,在iPhone中,需要使用performSelectorOnMainThread委托主线程处理。
总结:
多线程能适当提高程序的执行效率,能适当提高资源利用率(CPU、内存利用率),但是开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能,而且线程越多,CPU在调度线程上的开销就越大,使用多线程程序设计更加复杂:比如线程之间的通信、多线程的数据共享
一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”
主线程的使用注意:别将比较耗时的操作放到主线程中。耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验
25. 线程是什么?进程是什么?二者有什么区别和联系? (UI第二十二讲 多线程编程)
线程是CPU独立运行和独立调度的基本单位(可以理解为一个进程中执行的代码片段),进程是资源分配的基本单位(进程是一块包含了某些资源的内存区域)。进程是线程的容器,真正完成代码执行的是线程,而进程则作为线程的执行环境。一个程序至少包含一个进程,一个进程至少包含一个线程,一个进程中的多个线程共享当前进程所拥有的资源。
26.谈谈你对多线程开发的理解?ios中有几种实现多线程的方法?(UI第二十二讲 多线程编程)
好处:
①、使用线程可以把程序中占据时间长的任务放到后台去处理,如图片、视频的下载
②、发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好
缺点:
①、大量的线程降低代码的可读性,
②、更多的线程需要更多的内存空间
③、当多个线程对同一个资源出现争夺的时候要注意线程安全的问题。
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语句块在子线程中执行
}
③、Grand Central Dispatch (GCD)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 耗时的操作
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});
});
PS:不显示的创建线程的方法:
用NSObject的类方法 performSelectorInBackground:withObject:
创建一个线程:
[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];
27.线程同步和异步的区别?IOS中如何实现多线程的同步?
同步:一个线程要等待上一个线程执行完之后才能执行当前的线程,生活中的例子(上厕所)。
异步:同时去做两件或者多件事。比如边听歌边看报。
.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];
51. GCD的队列(dispatch_queue_t)分哪两种类型?
串行队列Serial Dispatch Queue
并行队列Concurrent Dispatch Queue
77. 你参与的APP,是如何处理多个服务的同步发起的?
使用iOS线程技术,创建并行队列来分别执行不同的任务。将不同的任务加入到子队列当中,任务执行后回到主线程当中刷新UI界面。
APP 界面一般都是根据产品需求和UI效果图来完成的,但是我们为了提高代码的复用性,会将视图的实现进行封装,然后在控制器当中进行加载调用
88.NSOperationQueue有哪些使用方式?
一种在iOS中执行并发操作的方法,是使用NSOperation和NSOperationQueue类。在本教程中,你将学习如何使用它们!你会先创建一款不使用多线程的app,这样它会变得响应非常迟钝。然后改进程序,添加上并行操作–并且希望–可以提供一个交互响应更好的界面给用户!
另一种处理操作之间的依赖关系,如果操作直接有依赖关系,比如第二个操作必须等第一个操作结束后再执行。控制线程池中的线程数
3. NSThread、NSOperationQueue以及GCD等多线程编程技术什么情况下使用?
1)NSThread
优点:NSThread比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
2)Cocoa NSOperation
优点:不需要关心线程管理, 数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation相关的类是NSOperation, NSOperationQueue.
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类: NSInvocationOperation和NSBlockOperation.
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。
44.项目中哪块用到多线程,碰到了什么问题,怎么解决的
什么时候使用多线程:
将耗时、轮询或者并发需求高等任务分配到其他线程执行,并由主线程负责统一更新界面会使得应用程序更加流畅,用户体验更好,例如网络请求,播放游戏的背景音乐,文件下载等。
碰到的问题以及解决办法:
(1)使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢
使用以下方法可以控制并发的线程数量:
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
使用addDependency可以建立操作之间的依赖关系,设定操作的执行顺序
(2)当多个线程对同一个资源出现争夺的时候需要注意线程安全问题
(3)更新UI界面,处理界面和用户之间的交互事件一定要在主线程中处理。
‘多线程中栈与堆是公有的还是私有的
一般来说栈是私有的,堆是公有的;但是可以为特定的线程创建私有的堆
在多线程环境下,每个线程拥有一个栈和一个程序计数器。栈和程序计数器用来保存线程的执行历史和线程的执行状态,是线程私有的资源。其他的资源(比如堆、地址空间、全局变量)是由同一个进程内的多个线程共享。
堆: 是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
栈:是个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是 thread safe的。操作系统在切换线程的时候会自动的切换栈,就是切换 SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
1、列举Cocoa中常见对几种线程的实现,并谈谈多线程安全的几种解决办法和多线程安全怎么控制.
Cocoa中常见对几种线程的实现:
(1)OC的NSThread
(2) C语言的GCD接口(性能最好,代码更精简)
(3) OC的NSOperation和NSOperationQueue(基于GCD)
多线程安全的几种解决办法
(1)只在主线程刷新访问UI
(2)如果要防止资源抢夺,得用synchronize进行加锁保护。
(3)如果异步操作要保证线程安全等问题,尽量使用GCD。(GCD有些函数默认就是安全的)
3. 2.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;
}
4、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与其他线程进行通讯。
子线程中使用了定时器
使用任何perform mSelector*****到子线程中运行方法
使用子线程去执行周期性任务
NSURLConnection在子线程中发起异步请求
10)OC中创建线程的方法是什么?如果在主线程中执行代码,方法是什么
线程创建有三种方法:使用NSThread创建、使用GCD的dispatch、使用子类化的NSOperation,然后将其加入NSOperationQueue;在主线程执行代码,方法是performSelectorOnMainThread,如果想延时执行代码可以用performSelector:onThread:withObject:waitUntilDone:
11)线程和进程的区别和联系
进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程
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参数的使用,会是代码更为易读,建议在简单项目中使用。
1、线程的堆栈大小
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的流畅度,给用户一种“卡”的坏体验
25. 线程是什么?进程是什么?二者有什么区别和联系? (UI第二十二讲 多线程编程)
线程是CPU独立运行和独立调度的基本单位(可以理解为一个进程中执行的代码片段),进程是资源分配的基本单位(进程是一块包含了某些资源的内存区域)。进程是线程的容器,真正完成代码执行的是线程,而进程则作为线程的执行环境。一个程序至少包含一个进程,一个进程至少包含一个线程,一个进程中的多个线程共享当前进程所拥有的资源。
26.谈谈你对多线程开发的理解?ios中有几种实现多线程的方法?(UI第二十二讲 多线程编程)
好处:
①、使用线程可以把程序中占据时间长的任务放到后台去处理,如图片、视频的下载
②、发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好
缺点:
①、大量的线程降低代码的可读性,
②、更多的线程需要更多的内存空间
③、当多个线程对同一个资源出现争夺的时候要注意线程安全的问题。
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语句块在子线程中执行
③、Grand Central Dispatch (GCD)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 耗时的操作
dispatch_async(dispatch_get_main_queue(), ^{
// 更新界面
});
});
PS:不显示的创建线程的方法:
用NSObject的类方法 performSelectorInBackground:withObject:
创建一个线程:
[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];
27.线程同步和异步的区别?IOS中如何实现多线程的同步?
同步:一个线程要等待上一个线程执行完之后才能执行当前的线程,生活中的例子(上厕所)。
异步:同时去做两件或者多件事。比如边听歌边看报。
.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];
51. GCD的队列(dispatch_queue_t)分哪两种类型?
串行队列Serial Dispatch Queue
并行队列Concurrent Dispatch Queue
77. 你参与的APP,是如何处理多个服务的同步发起的?
使用iOS线程技术,创建并行队列来分别执行不同的任务。将不同的任务加入到子队列当中,任务执行后回到主线程当中刷新UI界面。
APP 界面一般都是根据产品需求和UI效果图来完成的,但是我们为了提高代码的复用性,会将视图的实现进行封装,然后在控制器当中进行加载调用
88.NSOperationQueue有哪些使用方式?
一种在iOS中执行并发操作的方法,是使用NSOperation和NSOperationQueue类。在本教程中,你将学习如何使用它们!你会先创建一款不使用多线程的app,这样它会变得响应非常迟钝。然后改进程序,添加上并行操作–并且希望–可以提供一个交互响应更好的界面给用户!
另一种处理操作之间的依赖关系,如果操作直接有依赖关系,比如第二个操作必须等第一个操作结束后再执行。控制线程池中的线程数
89.NSThread中的Runloop的作用,如何使用?
有些情况下,我们还是在运行一些长线任务或者复杂任务的时候需要用比较原始的NSThread。这就需要为NSThread创建一个run loop.
1 NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(playerThread: ) object:nil];
2 [thread start];
3 //如果要利用NSOperation,原理类似。只需要加入到queue里面去就好了。。queue会在合适的时机调用方法,下面代码作为参考。
4 - (void) playerThread: (void*)unused
5 {
6 audioRunLoop = CFRunLoopGetCurrent();//子线程的runloop引用
7 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];//子线程的
8 [self initPlayer]; CFRunLoopRun(); //运行子线程的
9 [pool release]; // run loop,这里就会停住了。
10 }
11 // 实现一个timer,用于检查子线程的工作状态,并在合适的时候做任务切换。或者是合适的时候停掉自己的
12 -(void) initPlayer {
13 // 在这里你可以初始化一个工作类,比如声音或者视频播放
14 NSTimer *stateChange = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:
15 @selector(checkStatuserInfo:nil repeats:YES];
16 }
17 -(void) checkState:(NSTimer*) timer
18 {
19 if(需要退出自线程了) {
20 //释放子线程里面的资源
21 CFRunLoopStop( CFRunLoopGetCurrent());//结束子线程任务
22 }
23 }
3. NSThread、NSOperationQueue以及GCD等多线程编程技术什么情况下使用?
1)NSThread
优点:NSThread比其他两个轻量级
缺点:需要自己管理线程的生命周期,线程同步。线程同步对数据的加锁会有一定的系统开销
2)Cocoa NSOperation
优点:不需要关心线程管理, 数据同步的事情,可以把精力放在自己需要执行的操作上。
Cocoa operation相关的类是NSOperation, NSOperationQueue.
NSOperation是个抽象类,使用它必须用它的子类,可以实现它或者使用它定义好的两个子类: NSInvocationOperation和NSBlockOperation.
创建NSOperation子类的对象,把对象添加到NSOperationQueue队列里执行。
2)GCD使用过程中遇到的问题?
44.项目中哪块用到多线程,碰到了什么问题,怎么解决的
什么时候使用多线程:
将耗时、轮询或者并发需求高等任务分配到其他线程执行,并由主线程负责统一更新界面会使得应用程序更加流畅,用户体验更好,例如网络请求,播放游戏的背景音乐,文件下载等。
碰到的问题以及解决办法:
(1)使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢
使用以下方法可以控制并发的线程数量:
-(void)setMaxConcurrentOperationCount:(NSInteger)cnt;
使用addDependency可以建立操作之间的依赖关系,设定操作的执行顺序
(2)当多个线程对同一个资源出现争夺的时候需要注意线程安全问题
(3)更新UI界面,处理界面和用户之间的交互事件一定要在主线程中处理。
‘多线程中栈与堆是公有的还是私有的
一般来说栈是私有的,堆是公有的;但是可以为特定的线程创建私有的堆
在多线程环境下,每个线程拥有一个栈和一个程序计数器。栈和程序计数器用来保存线程的执行历史和线程的执行状态,是线程私有的资源。其他的资源(比如堆、地址空间、全局变量)是由同一个进程内的多个线程共享。
堆: 是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
栈:是个线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是 thread safe的。操作系统在切换线程的时候会自动的切换栈,就是切换 SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。
24、描述runloop和线程的关系
Run loop,正如其名,loop表⽰示某种循环,和run放在⼀一起就表⽰示⼀一直在运⾏行着 的循环。实际上,run loop和线程是紧密相连的,可以这样说run loop是为了线 程⽽而⽣生,没有线程,它就没有存在的必要。Run loops是线程的基础架构部分, Cocoa和CoreFundation都提供了run loop对象⽅方便配置和管理线程的run loop(以
下都已Cocoa为例)。每个线程,包括程序的主线程(main thread)都有与之相 应的run loop对象。
8、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、撤消进程法
解决策略:鸵鸟策略、预防策略、避免策略、检测与解除死锁