美文网首页
iOS-多线程-NSThread

iOS-多线程-NSThread

作者: Imkata | 来源:发表于2020-08-14 09:15 被阅读0次

一. NSThread的创建方式

1. 类方法创建

类方法创建不需要手动开启线程

//detach派遣
[NSThread detachNewThreadSelector:@selector(threadMain1:) toTarget:selfwithObject:@(20)];

2. 实例方法创建

实例方法创建就要手动开启线程,否则不会调用threadMain2方法

NSThread *thread = [[NSThread alloc] initWithTarget:selfselector:@selector(threadMain2:) object:dict];
[thread start];

3. 隐式创建

隐式创建会自动启动,也不需要手动开启线程

[self performSelectorInbackground:@selector(download:) withObject:nil];

类方法创建和隐式创建的优缺点:

  1. 优点:简单快捷
  2. 缺点:无法对线程进行更详细的设置

二. 线程结束之后的通知

线程结束之后系统会发送通知,要先判断是哪个线程的通知,然后再进行操作。

[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(threadFinish:) name:NSThreadWillExitNotification object:nil];

要在threadFinish方法里面通过 NSThread *thread = [NSThread currentThread] 判断是哪个线程结束了,然后再进行操作。

三. 线程之间的通信

在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信。

线程间通信的体现:

  1. 一个线程传递数据给另一个线程
  2. 在一个线程中执行完特定任务后,转到另一个线程继续执行任务

线程间通信常用方法:

// 到主线程执行任务
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// 到指定线程执行任务
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 在子线程中调用download方法下载图片
[self performSelectorInBackground:@selector(download) withObject:nil];

示意图如下:

线程间通信.png

四. 线程相关方法

// 获取主线程
+ (NSThread *)mainThread; 
// 是否为主线程
- (BOOL)isMainThread; 
// 是否为主线程
+ (BOOL)isMainThread; 
// 获得当前线程
NSThread *current = [NSThread currentThread]; 
// 线程的调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高
// 获取优先级
+ (double)threadPriority;
// 设置优先级
+ (BOOL)setThreadPriority:(double)p;
// 设置线程的名字
- (void)setName:(NSString *)n;
// 获取线程的名字
- (NSString *)name;

五. 线程的状态

1. 线程的创建

self.thread=[[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil];
// 创建线程有多种方式,这里不做过多的介绍。
线程的创建.png

2. 线程的开启

[self.thread start];
线程的状态.png

3. 线程的运行和阻塞

  1. 设置线程阻塞,阻塞2秒
[NSThread sleepForTimeInterval:2.0];
  1. 设置线程阻塞,传入一个date
NSDate *date=[NSDate dateWithTimeIntervalSinceNow:4.0];
[NSThread sleepUntilDate:date];
线程阻塞.png

线程处理阻塞状态时在内存中的表现情况:线程被移出可调度线程池,此时不可调度。

被移出.png

4. 线程的死亡

当线程的任务结束,发生异常,或者是强制退出这三种情况会导致线程的死亡。

线程死亡.png

线程死亡后,线程对象从内存中移除。

从内存中移除.png

结束线程:

[NSThread exit];

注意:人死不能复生,线程死了也不能复生(重新开启),如果在线程死亡之后,再次调用[self.thread start]尝试重新开启线程,则程序会挂,如下图:

线程死亡.png

六. 线程相关练习

1. 小练习1

线程1一直运行,线程2打印,线程2结束之后通知线程1结束,线程1结束之后播放音效。

#import "ViewController.h"
#import <AudioToolbox/AudioToolbox.h> //需要导入audiotoolbox框架
@interface ViewController ()
{
  NSLock *threadLock;
}
@end
 
@implementation ViewController
 
- (void)viewDidLoad {
    [super viewDidLoad];
    [self createUI];
 
    threadLock = [[NSLock alloc]init];
    //线程结束之后的通知  系统自己发送的通知
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(threadFinish:) name:NSThreadWillExitNotification object:nil];
}
 
- (void)pressBtn:(UIButton *)button
{
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain1:) object:nil];
    thread.name = @"线程1";
    [thread start];
    NSDictionary *dict = @{@"thread":thread,@"num":@(100)};
    NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain2:) object:dict];
    thread2.name = @"线程2";
    [thread2 start];
}
 
- (void)threadMain1:(id)num
{
    [threadLock lock];
    int i = 0;
    while (1) {
        NSLog(@"线程1 = %d",i ++);
        [NSThread sleepForTimeInterval:1];
        //判断是否能接收到cancel消息接收到消息就从cpu中消失
        if ([[NSThread currentThread] isCancelled]) {
            NSLog(@"线程1接收到cancel消息");
            [NSThread exit];
        }
    }
    [threadLock unlock];
}
 
- (void)threadMain2:(NSDictionary *)dict
{
    [threadLock lock];
    NSLog(@"%@开始",[[NSThread currentThread] name]);
    for (int i =0; i <[dict[@"num"] integerValue]; i ++) {
        NSLog(@"线程2 = %d",i +1);
        [NSThread sleepForTimeInterval:0.1];
    }
    NSLog(@"线程2结束");

    //线程2结束让线程1立即停止
    [dict[@"thread"] cancel];
    [threadLock unlock]; 
}

- (void)threadFinish:(NSNotification *)notify
{
    NSThread *thread = [NSThread currentThread];
    NSLog(@"线程%@结束",thread.name);
    if thread.name = @"线程2" {
    //添加线程结束通知音
    NSString *path = [[NSBundle mainBundle]pathForResource:@"sound" ofType:@"wav"];
    NSURL *url = [NSURL fileURLWithPath:path];
    SystemSoundID SID;
    AudioServicesCreateSystemSoundID((__bridge CFURLRef)url, &SID);
    AudioServicesPlaySystemSound(SID);
     }
}

2. 小练习2

一般情况下主线程处理UI操作,子线程处理一些耗时的操作,为了防止一些耗时的操作卡死主线程,我们可以在子线程中处理耗时的操作,然后通过performSelectorOnMainThread回到主线程刷新UI。

#import "XLViewController.h"
 
@interface XLViewController ()
{
    UIProgressView * progressView;
}
@end
 
@implementation XLViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self createUIButton];
    
    progressView = [[UIProgressView alloc]initWithFrame:CGRectMake(10, 50, self.view.frame.size.width - 20, 10)];
    progressView.progress = 0;
    [self.view addSubview:progressView];
}

-(void)createUIButton
{
    UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(100, 100, 100, 100);
    [btn setTitle:@"线程" forState:UIControlStateNormal];
    [btn setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
    [btn addTarget:self action:@selector(pressBtn:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn];
}

-(void)pressBtn:(id)sender
{
    // 创建子线程,防止主线程出现假死的情况
    [NSThread detachNewThreadSelector:@selector(threadMain:) toTarget:self withObject:@(100)];
}

-(void)threadMain:(NSNumber *)num
{
    for(int i = 0;i<=num.intValue;i++)
    {
        // progressView.progress = i / num.floatValue;
        // NSLog(@"%f",progressView.progress);
        
        // 回到主线程刷新UI
        [self performSelectorOnMainThread:@selector(changeProgressView:) withObject:@(i) waitUntilDone:NO];
        [NSThread sleepForTimeInterval:0.1];
    }
}

-(void)changeProgressView:(NSNumber *)num
{
    progressView.progress = num.floatValue / 100;
    NSLog(@"%f",progressView.progress);
}
@end

相关文章

网友评论

      本文标题:iOS-多线程-NSThread

      本文链接:https://www.haomeiwen.com/subject/jzwedktx.html