iOS多线程开发
2015-06-22 22:56
381 查看
进程和线程的基本知识
进程:在面向多线程设计的系统中(如当代多数操作系统,当然也包括iOS,Mac OS X),一个运行的程序就是一个进程。每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。进程本身不是一个执行单位,而是线程的容器。线程:线程是进程内部的一个执行单元。系统创建好进程后,实际上就启动执行了该进程的主线程,主线程以函数地址形式,比如说main或WinMain函数,将程序的启动点提供给操作系统。主线程终止了,进程也就随之终止。每个线程只有一些运行必备的资源,调用栈(call stack),寄存器环境(register context),线程本地存储(thread-local storage)。多个线程共享所属进程的系统资源。
每一个进程至少有一个主线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。一个进程中的所有线程都在该进程的虚拟地址空间中,共同使用这些虚拟地址空间、全局变量和系统资源,所以线程间的通讯非常方便,多线程技术的应用也较为广泛。多线程可以实现并行处理,避免了某项任务长时间占用CPU时间。在单核CPU的机器中,为了运行所有这些线程,操作系统为每个独立线程安排一些CPU时间,操作系统以轮换方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。
多线程的应用:我们可以把耗时的数据计算和IO操作放在子线程上执行,保证主线程响应用户操作。当数据计算/IO操作执行完再通知主线程做UI的更新。这样就大大的提高了资源利用率。
注意:多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。
多线程的问题:
1、如果两个非常活跃的线程为了抢夺对CPU的控制权,在线程切换时会消耗很多的CPU资源,反而会降低系统的性能。
2、在iOS系统中,主线程堆栈大小为1M,子线程堆栈大小为512K。如果创建过多的线程,会造成大量的内存和CPU时间的消耗。
3、因为线程不拥有系统资源,多个线程共享进程的资源,在两个以上线程同时操作相同资源时,容易造成死锁或者制造出垃圾数据。
所以在你想要使用多线程进行编程的时候,一定要想清楚是否必要,切勿滥用。
iOS多线程编程
在iOS开发中,有三种方式多线程技术:NSThread,NSOperation,GCD。这三种技术随着Mac/iOS发展,一步步引入的。下面我们逐一进行分析。并总结出每种技术的使用环境。[b]NSThread[/b]
NSThread是轻量级线程类,需要我们自己管理线程的生命周期。
创建线程的几种方式:
显示创建
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
通过类方法创建一个线程,并自动启动
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
通过对象方法创建一个线程,需要手动调用线程的start方法来启动
隐式创建
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
这个是NSObject类目(category)方法,APPLE为所有继承NSObject的类添加了这个方法。这个方法隐式地创建了一个后台线程来执行aSelector。同样不需要手动调用start来启动
线程通信
通常我们启动新线程是用来执行耗时的操作,以避免阻塞主线程,当执行完这些耗时操作后,通知主线程来做某些显示操作。
比如,现在我们在要从网络中获取图片并显示在View上的一个需求。我们就可以启动一个线程来帮我们完成下载图片的操作,当下载完成后,再通知主线程显示图片。
通知主线程的方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
还有一种是,一个线程通知另一个线程执行某操作,这种情况很少,不做介绍。方法如下
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait
优点:
1、轻量级,当执行简单的多线程操作的时候,我们可以使用NSThread。经常使用的NSObject封装的线程方法
缺点:
1、需要我们自己管理线程生命周期
2、不能实现线程的依赖,比如我们启用两个线程1、2,需要等待线程1完成后再执行线程2的操作。这个时候,我们用NSThread就很没办法实现了
3、在前面我们知道启动一个线程需要占用内存(主线程1M,子线程512K),当我们启用线程过多,势必会占用大量的内存,得不偿失
已知上面的缺点,引出下面我们要讨论的另一种多线程方式NSOperation
[b]NSOperation[/b]
NSOperation晚于NSThread出现,增添许多NSThread没有的特性。比如NSOperation可以设置最大线程数,可以设置线程依赖等等。
NSOperation是抽象基类(需要注意的是,OC和其他如C++,C#等语言不可生成抽象类对象不同。我们仍然可以生成NSOperation的对象),通常我们使用NSOperation的子类:NSInvocationOperation,NSBlockOperation,这两个子类本质上没有区别,只是NSBlockOperation通过block方式来组织代码,代码结构相对清晰。当我们定义好需要的操作的时候,需要生成一个操作队列:NSOperationQueue,把我们前面生成的NSInvocationOperation或NSBlockOperation添加到队列中,就可以执行,我们不需要手动启用线程。NSOperationQueue就是一个线程池,具体线程池哪个线程来执行这个操作,我们不需要关系,只要把操作添加进去就行了。
假如我们有一个需求。我们要下载两张照片1、2,照片1必须在照片2下载完,才执行下载照片1的操作。
我们可以声明两个操作1、2,让操作1依赖操作2。便可以实现上面的需求。
为了避免文章太长,我们就把上面所说到的知识点全用上,写了以下代码。里面有注释,不难,大家肯定可以看得懂😄
- (void)viewDidLoad { [super viewDidLoad]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 2; // 最大线程数为2 // 生成NSInvocationOperation NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downloadDataFromNet) object:nil]; [queue addOperation:operation]; // 把operation添加到执行队列queue中 // 生成NSBlockOperation NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%s:%@", __FUNCTION__, [NSThread currentThread]); }]; [operation addDependency:blockOperation]; // operation依赖blockOperation,当blockOperation执行完才会执行operation,这样我们就强制操作的执行顺序 [queue addOperation:blockOperation]; // 把blockOperation添加到执行队列queue中 } - (void)downloadDataFromNet{ // NSLog(@"%d", [NSThread isMultiThreaded]); NSLog(@"%s:%@", __FUNCTION__, [NSThread currentThread]); }
如果APPLE提供的NSInvocationOperation,NSBlockOperation不能满足你,你可以自定义一个操作MyOperation,继承NSOperation。我们需要重写NSOperation的方法main等方法,在这先不做介绍。
优点:
1、隐藏了复杂的线程生命周期的管理
2、可以明确线程依赖关系
3、可以设置最大线程数
4、可以取消任务执行
[b]GCD[/b]
[b]待续。。。。。。[/b]
相关文章推荐
- IOS引用私有private framework API 图解
- iOS 牛人技术博客分享
- iOS开发之头像裁剪
- iOS开发之使用CALayer封装下载进度条
- iOS开发之那些不容错过的技术博客
- 谈谈iOS中粘性动画以及果冻效果的实现
- iOS ARC的打开 与 关闭
- ios 清理cach缓存
- IOS子视图超过父视图frame后,无法交互响应
- nagios插件之监控if8接口日志(新接口)
- Event handling for iOS - how hitTest:withEvent: and pointInside:withEvent: are related?
- (六十五)iOS的socket实现(GCDAsyncSocket)
- (六十五)iOS的socket实现(GCDAsyncSocket)
- OC基础:类的扩展.协议 分类: ios学习 OC 2015-06-22 19:22 34人阅读 评论(0) 收藏
- OC基础:Date 分类: ios学习 OC 2015-06-22 19:16 158人阅读 评论(0) 收藏
- OC基础:block.字面量 分类: ios学习 OC 2015-06-22 19:08 155人阅读 评论(0) 收藏
- (六十四)iOS的socket实现(C+OC混合实现)
- (六十四)iOS的socket实现(C+OC混合实现)
- 【iOS】Mapkit的使用:地图显示、定位、大头针、气泡等
- iOS 观察者模式(KVO)的简单使用