iOS多线程编程--NSThread

前言:

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在网络编程中,多线程编程是一种常用的技术,可以提高程序的并发性和性能。下面是一些关于多线程编程的常用方法和注意事项: 1. NSThreadNSThreadiOS中最底层的线程类,它可以通过类方法或实例方法来创建线程。使用NSThread可以设置线程的名称、优先级,以及控制线程的睡眠和退出等操作。 2. 线程调度:在多线程编程中,多个线程会并发运行,但线程的执行顺序是由CPU调度器决定的,程序员无法控制。多个线程会同时竞争CPU资源,谁先抢到资源谁就先执行,所以多线程的执行顺序是随机的。 3. 多线程的创建:在iOS开发中,常用的多线程编程方式有三种:NSThread、GCD和NSOperation。NSThread是最底层的线程类,可以直接操作线程的各种属性和方法。GCD(Grand Central Dispatch)提供了一种高效的并发编程模型,可以通过队列来管理任务的执行。NSOperation是基于GCD的更高层次的封装,提供了更多的控制和管理线程的功能。 4. 线程的创建顺序:在多线程编程中,并不能保证哪个线程会先运行,即无法确定新创建的线程或调用线程哪个会先执行。新创建的线程可以访问进程的地址空间,并继承调用线程的浮点环境和信号屏蔽字,但挂起信号集会被清除。 总结来说,多线程编程是一种提高程序并发性和性能的技术,在网络编程中尤为重要。通过使用NSThread、GCD或NSOperation等方法,可以实现多线程的创建和管理。然而,程序员无法控制线程的执行顺序,因为线程的调度是由CPU调度器决定的。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [IOS多线程基础(OC)](https://blog.csdn.net/yong_19930826/article/details/105857055)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [UNIX环境高级编程笔记](https://blog.csdn.net/w_x_myself/article/details/128613534)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值