原先用新浪写的博客,现在转到这里了,虽然是从13年开始写的,可是换了电脑之后就只记得这一个的账号和密码了
http://blog.sina.com.cn/s/articlelist_2730732307_0_1.html
最近总有朋友问我多线程的问题,所以把大概的知识点整理一下,方便大家查看,有写的不对的地方欢迎大家指出~~
程序(Program)是一个可以运行的文件, 一个程序至少有一个进程,一个进程至少有一个线程,即主线程
进程:正在进行的程序被称为进程,负责程序运行的内存分配,每个进程都有自己的独立虚拟内存空间.一个程序的一次运行,在执行过程中拥有独立的内存单元,而多个线程共享一块内存
什么是线程:线程是进程中的基本单元(可执行的代码段),线程可以并发运行,提高执行效率
创建线程的目的:就是为了开启一条新的可执行的代码段,与主线程中的代码实现同时运行,防止界面假死,是实现异步的技术的主要手段,比如网络异步下载
进程和线程的区别和联系:
联系:线程是进程的基本组成单位,要想在进程中执行任务,就必须开启线程,一条线程就代表一个任务,一个进程中允许开启多条线程,也就是同时执行多个任务。进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。
区别:(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源 :进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
举例说明:操作系统有多个软件在运行(QQ、office、音乐等),这些都是一个个进程,而每个进程里又有好多线程(比如QQ,你可以同时聊天,发送文件等)
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
iOS的三种多线程技术
1.多线程是个复杂的概念,按字面意思是同步完成多项任务,提高了资源的使用效率
2.多线程的作用:可以把占据时间长的程序中的任务放到后台去处理;用户界面可以更加吸引人(这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度);可以解决负载均衡问题,充分利用cpu资源。为了提高CPU的使用率,采用多线程的方式去同时完成几件事情而互不干扰
3.大多情况下,要用到多线程的主要是需要处理大量的IO操作时或处理的情况需要花大量的时间等等,比如:读写文件、视频图像的采集、处理、显示、保存等好处:
缺点:
1.如果有大量的线程,会影响性能,因为操作系统需要在它们之间切换。
2.更多的线程需要更多的内存空间。
3.线程的中止需要考虑其对程序运行的影响。
4.通常块模型数据是在多个线程间共享的,需要防止线程死锁情况的发生。
多线程的底层实现?
1> 首先搞清楚什么是线程、什么是多线程、多线程的使用场合
2> Mach是第一个以多线程方式处理任务的系统,因此多线程的底层实现机制是基于Mach的线程
3> 开发中很少用Mach级的线程,因为Mach级的线程没有提供多线程的基本特征,线程之间是独立的
4> 开发中实现多线程的4种方案
1.NSThread
• 使用NSThread对象建立一个线程非常方便,但管理多个线程非常困难,不推荐使用
• 使用[NSThreadcurrentThread]跟踪任务所在线程,适用于这三种技术
2.NSOperation/NSOperationQueue是使用GCD实现的一套Objective-C的API是面向对象的线程技术
• 提供了一些在GCD中不容易实现的特性,如:限制最大并发数量、操作之间的依赖关系、方便地调整执行顺序
• NSOperationQueue支持KVO,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)
3.GCD —— Grand Central DispatchGCD的基本思想就是将操作s放在队列s中去执行
(1)是基于C语言的底层API ,具有运行时的特点,用Block定义任务,使用起来非常灵活便捷
(2)GCD需要写很多的代码才能实现队列间设置依赖关系,GCD的执行速度比NSOperationQueue快
(3)提供了更多的控制能力以及操作队列中所不能使用的底层函数
(4)队列负责调度任务执行所在的线程以及具体的执行时间
(5)队列的特点是底层函数先进先出(FIFO)的,新添加至队列的操作都会排在队尾
任务之间不太互相依赖:GCD
任务之间有依赖\或者要监听任务的执行情况:NSOperationQueue
项目中使用NSOperation的优点是NSOperation是对线程的高度抽象,在项目中使用它,会使项目的程序结构更好,子类化NSOperation的设计思路,是具有面向对象的优点(复用、封装),使得实现是多线程支持,而接口简单,建议在复杂项目中使用。
项目中使用GCD的优点是GCD本身非常简单、易用,对于不复杂的多线程操作,会节省代码量,而Block参数的使用,会是代码更为易读,建议在简单项目中使用
从基本功能上讲,GCD有点像NSOperationQueue,他们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行。GCD比之NSOpertionQueue更底层更高效,并且它不是Cocoa框架的一部分。GCD的API很大程度上基于block,当然,GCD也可以脱离block来使用,比如使用传统c机制提供函数指针和上下文指针。实践证明,当配合block使用时,GCD非常简单易用且能发挥其最大能力。
4.C语言的POSIX接口:#include <pthread.h>
*提示
GCD 的函数都是以dispatch开头的
*队列
dispatch_queue_t
串行队列,队列中的任务只会顺序执行
并行队列,队列中的任务通常会并发执行
*操作
dispatch_async 异步操作,会并发执行,无法确定任务的执行顺序(DISPATCH_QUEUE_CONCURRENT)
dispatch_sync 同步操作,会依次顺序执行,能够决定任务的执行顺序(DISPATCH_QUEUE_SERIAL)
同步操作不会新建线程、操作顺序执行(没用!)同步任务中包含同步会造成线程阻塞
异步操作会新建线程、操作顺序执行(非常有用!)
(5)回到主线程的方法:
1)dispatch_async(dispatch_get_main_queue(), ^{ });
2) [selfperformSelectorOnMainThread: withObject: waitUntilDone:];
3) [self performSelector: onThread:[NSThreadmainThread] withObject: waitUntilDone:];
作用:主线程是显示UI界面,子线程多数是进行数据处理主队列(线程)
1>每一个应用程序都只有一个主线程
2>所有UI的更新工作,都必须在主线程上执行!
3>主线程是一直工作的,而且除非将程序杀掉,否则主线程的工作永远不会结束!
(6)延迟执行的方法:
1) [self performSelector: withObject:afterDelay:];
2) [NSTimer scheduledTimerWithTimeInterval:target: selector: userInfo: repeats:];
3) double delayInSeconds = 2.0;
dispatch_time_t popTime =dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime,dispatch_get_main_queue(), ^(void){
});
(7)线程间怎么通信?
1> GCD
2>performSelector:onThread:withObject:waitUntilDone:如果想延时执行代码可以用
performSelectorOnMainThread:withObject:waitUntilDone:在主线程执行代码
3> NSMachPort(可选)
基本机制:A线程(父线程)创建NSMachPort对象,并加入A线程的run loop。当创建B线程(辅助线程)时,将创建的NSMachPort对象传递到主体入口点,B线程(辅助线程)就可以使用相同的端口对象将消息传回A线程(父线程)。
(8)网络图片处理问题中怎么解决一个相同的网络地址重复请求的问题?
利用字典(图片地址为key,下载操作为value)
(9)用NSOpertion和NSOpertionQueue处理A,B,C三个线程,要求执行完A,B后才能执行C,怎么做?
// 创建队列
NSOperationQueue *queue =[[NSOperationQueue alloc] init];
// 创建3个操作
NSOperation *a = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@”operation1---“);
}];
NSOperation *b = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@”operation1---“);
}];
NSOperation *c = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@”operation1---“);
}];
// 添加依赖
// 设定执行顺序,Dependency依赖,可能会开多个,但不会太多
// 依赖关系是可以跨队列的!
[c addDependency:a];
[c addDependency:b];
// 执行操作 // GCD是串行队列,异步任务,只会开一个线程
[queue addOperation:a];
[queue addOperation:b];
[queue addOperation:c];
// 所有UI的更新需要在主线程上进行
[[NSOperationQueue mainQueue] addOperation:op4];
(10)列举cocoa中常见对几种多线程的实现,并谈谈多线程安全的几种解决办法及多线程安全怎么控制?
1> 只在主线程刷新访问UI
2> 如果要防止资源抢夺,得用synchronized进行加锁保护
3> 如果异步操作要保证线程安全等问题,尽量使用GCD(有些函数默认就是安全的)
4> 注意对代码正确性的威胁(保证数据同步安全,以及防止线程死锁)
使用锁:锁是线程编程同步工具的基础。锁可以让你很容易保护代码中一大块区域以便你可以确保代码的正确性。使用POSIX互斥锁;使用NSLock类;使用@synchronized指令等。
线程安全总结:
a. 不可改变的对象,通常是线程安全的
b. 主线程负责处理响应事件
线程安全的类和函数: NSArray, NSData, NSNumber.....
非线程安全: NSBundle, NSCoder, NSArchiver, NSMutableArray
(11)GCD内部怎么实现的
1> iOS和OS X的核心是XNU内核,GCD是基于XNU内核实现的
2> GCD的API全部在libdispatch库中
3> GCD的底层实现主要有Dispatch Queue和Dispatch Source
Dispatch Queue :管理block(操作)
Dispatch Source :处理事件(比如线程间的通信)
(12) 既然提到GCD,那么在使用GCD以及block时要注意些什么?它们两是一回事儿么?block在ARC中和传统的MRC中的行为和用法有没有什么区别,需要注意些什么?
Block的使用注意:
1. block的内存管理:(注意循环引用,默认在栈中(不需要内存管理),通过copy就在在堆中,就要注意内存管理)
2. 防止循环retian
非ARC(MRC):__block
ARC:__weak\__unsafe_unretained
(13)在异步线程中下载很多图片,如果失败了,该如何处理?请结合RunLoop来谈谈解决方案
1> 重新下载图片
2> 下载完毕, 利用RunLoop的输入源回到主线程刷新UIImageVIUew
(14)如何用GCD同步若干个异步调用?(如根据若干个url异步加载多张图片,然后在都下载完成后合成一张整图)
使用Dispatch Group追加block到GlobalGroup Queue,这些block如果全部执行完毕,就会执行MainDispatch Queue中的结束处理的block。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*加载图片1 */ });
dispatch_group_async(group, queue, ^{ /*加载图片2 */ });
dispatch_group_async(group, queue, ^{ /*加载图片3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(),^{
// 合并图片
});
(15)根据什么确定要开启线程数?一般情况下开启几条?
答:一般不需要管理线程数量,程序开放时是针对队列开发的,向队列添加操作就可以了。具体的线程数量由底层的线程池管理,开启线程的数量一般会根据用户的网络情况决定。WIFI一般5-6条,3G一般2-3条。