iOS多线程编程--NSThread

来源:互联网 发布:淘宝单号充值 编辑:程序博客网 时间:2024/05/29 09:13

前言:

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的用法是一个道理。

五、线程的同步/锁

后续补上。

0 0
原创粉丝点击