iOS多线程编程--NSThread
2016-06-14 23:39
501 查看
前言:
OS 支持多个层次的多线程 编程,层次越高的抽象程度越高,使用起来也越方便,对于开发者来说,推荐使用更方便的GCD和NSOperation来进行多线程开发。但是本文主要讲解的是NSThread的使用,通过NSThread可以相对深入理解多线程的原理。
Thread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,你需要自己管理线程的生命周期,线程间的同步问题。
线程共享同一应用程序的部分内存空间,它们拥有对数据相同的访问权限,你得协调多个线程对同一数据的访问,一般做法是在访问之前加锁,这会导致一定的性能开销。
在 iOS 中我们可以使用多种形式的 thread:
1、Cocoa threads: 使用NSThread,直接从NSObject的类方法performSelectorInBackground:withObject: 来创建一个线程。 如果你选择thread来实现多线程,那么NSThread就是官方推荐优先选用的方式。 2、POSIX threads: 基于 C 语言的一个多线程库。
下面我们先来看看 NSThread 多线程的使用。
从线程创建与启动、线程的同步与锁、线程的交互、线程池等等四个方面来详解多线程。
1、优点:NSThread比其他两种多线程方案较轻量级,更直观地控制线程对象 2、缺点:需要自己管理线程的生命周期,线程同步。 线程同步对数据的加锁会有一定的系统开销。
了解GCD点击这里
了解NSOperation点击这里
一、线程的创建和启动
1、动态方法
//声明 - (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument; // 初始化线程 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; //线程执行的方法,这个selector最多只能接收一个参数 // 设置线程的优先级(0.0 - 1.0,1.0最高级) thread.threadPriority = 1; // 开启线程 [thread start]; //这种方式创建,需要手动启动线程
2、静态方法
//声明 + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument; [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil]; // 调用完毕后,会马上创建并开启新线程
3、隐式创建方法
[self performSelectorInBackground:@selector(run) withObject:nil];
4、区别
第 1 种方式会直接创建线程,并且开始运行线程,而且无需为线程的清理负责;第 2 种方式是创建线程对象后,需要手动启动线程,在运行线程操作前,可以对线程进行配置,比如设置 stack 大小,线程的优先级。
二、线程的操作方法
1、线程的获取
//返回当前线程 NSThread *current = [NSThread currentThread]; //返回主线程 NSThread *mainT = [NSThread mainThread];
2、线程的判断
// 判断是否为多线程 + (BOOL)isMultiThreaded; // 判断当前线程是否为主线程 - (BOOL)isMainThread; + (BOOL)isMainThread;
2、线程的配置
// 线程优先级 + (double)threadPriority ; + (BOOL)setThreadPriority:(double)p ; // 线程函数地址 + (NSArray *)callStackReturnAddresses; // 线程堆栈 - (NSUInteger)stackSize; - (void)setStackSize:(NSUInteger)s; // 设置与返回线程名称 - (void)setName:(NSString *)n; - (NSString *)name;
3、线程的暂停、取消
//休眠 NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]]; [NSThread sleepUntilDate:date]; // 暂停2s [NSThread sleepForTimeInterval:2]; // 退出线程 + (void)exit; // 取消操作 - (void)cancel; // 线程启动 - (void)start; // 线程执行入口 - (void)main; // 是否在执行 - (BOOL)isExecuting; // 是否已经结束 - (BOOL)isFinished; // 是否取消的 - (BOOL)isCancelled;
三、线程间的通信
1、在主线程执行操作
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES]; //waitUntilDone是指是否等到主线程把方法执行完了,这个performSelector方法才返回。 //指定线程的run loop 执行模式 [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES modes:NSDefaultRunLoopMode];
2、在指定线程上执行操作
这些performSelector形式的方法,都需要对方线程的RunLoop处于开启状态,因为这些方法实质是runloop的输入源,把消息发送给对方线程的runloop,然后对方从runloop里面获取消息,才去执行方法。主线程的runloop是默认开启的,副线程的runloop是默认构建,但是需要手动开启。
了解RunLoop点击这里
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES]; //指定线程的run loop 执行模式 [self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES modes:NSDefaultRunLoopMode];
3、在当前线程执行操作
[self performSelector:@selector(run) withObject:nil]; //指定线程的run loop 执行模式 [self performSelector:@selector(run) withObject:nil inModes:NSDefaultRunLoopMode];
4、线程的关闭
需要关闭某个线程,可以给这个线程发消息使其关闭:- (void)killThread { [self performSelector:@selector(exitThread:) onThread:_thread1 withObject:_thread1 waitUntilDone:NO]; } -(void)exitThread:(NSThread *)thread { [NSThread exit]; }
或者通过下面的方法使其关闭:
-(void )threadOneMethod{ //前面写线程需要执行任务的代码,最后进入runloop循环,保持线程不结束同时保持接受消息。 NSRunLoop *theRL = [NSRunLoop currentRunLoop]; while (shouldKeepRunning ){//shouldKeepRunning判断是否继续进行循环,如果为NO,就会停止循环,然后继续向下运行,线程自然结束 NSLog(@"looprun"); [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; }; NSLog(@"thread1 end"); } BOOL shouldKeepRunning = YES;//一个全局的BOOL类型变量 - (void)killThread { shouldKeepRunning = NO; }
这里是通过一个全局变量的改变来控制线程的继续还是结束。但是有个小问题是,当线程的runloop接受了外来的输入源之后,例如其他线程调用:
[self performSelector:@selector(timerFire) onThread:_thread1 withObject:nil waitUntilDone:NO];
在这个线程运行,runloop接受到消息后会阻塞在方法[theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]里面,也即是说while循环不会继续向下一个循环进行,那么改变shouldKeepRunning就不能马上得到反馈,所以需要使用:
BOOL shouldKeepRunning = YES; - (void)killThread { [self performSelector:@selector(exitThread:) onThread:_thread1 withObject:nil waitUntilDone:NO]; } -(void)exitThread:(NSThread *)thread{ shouldKeepRunning = NO; }
这样就是给要关闭的线程发消息,会立刻唤醒目标线程的runloop,因为[theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]方法的特性是在接触到输入源后方法立刻返回,这样while循环就会立刻进入进入下一个循环,也就会进行循环条件的判断,然后因为shouldKeepRunning变为NO了,就会退出循环,然后线程结束。
5、取消发送给当前 线程 的某个消息
cancelPreviousPerformRequestsWithTarget: cancelPreviousPerformRequestsWithTarget:selector:object:
四、多线程的使用举例
对于使用线程的一些建议:1、当我们需要中途停止线程时,我们不应该调用exit方法,而是调用cancel方法。
因为,如果我们直接调用exit方法的话,线程是直接退出,而没有机会去执行清理操作,可能会产生内存泄漏!
2、我们必须要清楚这么一个现象!
当线程在执行过程中,如果被sleepForTimeInterval后,线程将会被进入休眠。
那么在它休眠期间又被cancel后,事实上,线程在醒来后,任然会执行完它的操作。
// 线程执行 - (void) threadEntryPoint{ @autoreleasepool { NSLog(@"Thread Entry Point"); while ([[NSThread currentThread] isCancelled] == NO){ [NSThread sleepForTimeInterval:10]; NSLog(@"Thread Loop"); } NSLog(@"Thread Finished"); } } // 停止线程 - (void) stopThread{ NSLog(@"Cancelling the Thread"); [self.myThread cancel]; NSLog(@"Releasing the thread"); self.myThread = nil; } 调用: - (void)viewDidAppear:(BOOL)animated{ // 创建线程 self.myThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadEntryPoint) object:nil]; // 开启线程 [self.myThread start]; // 让线程3秒后取消 [self performSelector:@selector(stopThread) withObject:nil afterDelay:3.0f]; }
输出: Thread Entry Point Cancelling the Thread Releasing the thread Thread Loop Thread Finished
分析1:
注意,最后还是输出了 “Thread Loop”这一句,我明明调用了[NSThread sleepForTimeInterval:10]; 方法让线程进入休眠状态。并且让线程已经执行了stopThread方法中的[self.myThread cancel];方法把线程给取消了。
但是,线程在被唤醒后,任然执行了后面的代码,输出了 “Thread Loop”这一句!
只有改良的办法:多加一层判断!!!
- (void) threadEntryPoint{ @autoreleasepool { NSLog(@"Thread Entry Point"); while ([[NSThread currentThread] isCancelled] == NO){ [NSThread sleepForTimeInterval:10]; if ([[NSThread currentThread] isCancelled] == NO){ // 做一个改进,在需要执行的代码中,多加一层判断。 NSLog(@"Thread Loop"); } } NSLog(@"Thread Finished"); } }
分析2:
这个用法的原理,其实跟NSOperation的isCancelled的用法是一个道理。五、线程的同步/锁
后续补上。相关文章推荐
- Python3写爬虫(四)多线程实现数据爬取
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- C#实现多线程的同步方法实例分析
- 浅谈chuck-lua中的多线程
- C#简单多线程同步和优先权用法实例
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- C#多线程编程中的锁系统(三)
- 解析C#多线程编程中异步多线程的实现及线程池的使用
- C#多线程学习之(六)互斥对象用法实例
- 基于一个应用程序多线程误用的分析详解
- C#多线程学习之(三)生产者和消费者用法分析
- C#多线程学习之(一)多线程的相关概念分析
- C#多线程之Thread中Thread.IsAlive属性用法分析
- 分享我在工作中遇到的多线程下导致RCW无法释放的问题