今天看啥
热点:

多线程-NSThread介绍,多线程-nsthread


NSThread简介

NSThread封装性差,最偏向于底层,主要基于thread使用,OC语言,需要程序员自己管理生命周期.
每一个NSThread对象代表着一个线程,理解NSThread更有利于理解多线程的含义.

NSThread API

线程创建

有返回值初始化方法有3种:

- (instancetype)init API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)) NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

init方法初始化线程并返回,线程的入口函数由Selector传入。线程创建出来之后需要手动调用-start方法启动
无返回值(隐式创建)初始化方法有2种:

+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

分离新的子线程,直接创建并启动一个线程去执行Selector中的方法,由于没有返回值,如果需要获取新创建的Thread,需要在执行的Selector中的方法中调用[NSThread currentThread]获取

线程操作

NSThread给线程提供的主要操作方法有启动,睡眠,取消,退出.

线程启动

我们使用init方法将线程创建出来之后,线程并不会立即运行,只有我们手动调用-start方法才会启动线程

- (void)start API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

注意:部分线程属性需要在启动前设置,线程启动之后再设置会无效。如qualityOfService(线程优先级)属性

线程睡眠

+ (void)sleepUntilDate:(NSDate *)date;//线程休眠(阻塞),休眠到指定日期时间
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;//线程休眠(阻塞),休眠到指定日期时长

看到sleepUntilDate:大家可能会想起runloop的runUntilDate:。他们都有阻塞线程的效果,但是阻塞之后的行为又有不一样的地方,使用的时候,我们需要根据具体需求选择合适的API。

sleepUntilDate:相当于执行一个sleep的任务。在执行过程中,即使有其他任务传入runloop,runloop也不会立即响应,必须sleep任务完成之后,才会响应其他任务 runUntilDate:虽然会阻塞线程,阻塞过程中并不妨碍新任务的执行。当有新任务的时候,会先执行接收到的新任务,新任务执行完之后,如果时间到了,再继续执行runUntilDate:之后的代码

例子:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.view.backgroundColor = [UIColor yellowColor];
    [self createWithDetach];
}

- (void)createWithDetach{
     //隐式创建
    [NSThread detachNewThreadSelector:@selector(delay:) toTarget:self withObject:@"date"];
    __weak typeof(self)weakSlef = self;
    [NSThread detachNewThreadWithBlock:^{
        NSLog(@"ssss");
        [weakSlef delay:nil];
    }];
}
- (void)delay:(id)parm{
    if ([@"date" isEqual:parm]) {
        [NSThread sleepForTimeInterval:10];//该方法后面的代码,10秒后执行
        NSLog(@"暂停10秒执行");
    }else{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"暂停2秒执行");
    }
}

阻塞<喎?https://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoNCBpZD0="线程取消">线程取消

- (void)cancel API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
不过大家千万不要被它的名字迷惑,调用cancel方法并不会立刻取消线程,它仅仅是将cancelled属性设置为YES。cancelled也仅仅是一个用于记录状态的属性。线程取消的功能需要我们在main函数中自己实现 要实现取消.的功能,我们需要自己在线程的main函数中定期检查isCancelled状态来判断线程是否需要退出,当isCancelled为YES的时候,我们手动退出。如果我们没有在main函数中检查isCancelled状态,那么调用cancel将没有任何意义.

错误用法示例:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.view.backgroundColor = [UIColor yellowColor];
    [self cancelThread];
}

-(void)cancelThread{
    NSThread * t1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadCancel:) object:@"取消线程"];
    t1.name = @"取消研究";
    [t1 start];
}
- (void)threadCancel:(id)parm{
    NSLog(@"取消前");
    [[NSThread currentThread] cancel];
    NSLog(@"取消后");
    if ([[NSThread currentThread] isCancelled])
        NSLog(@"取消了");
}

错误用法
可以看到并没有取消,虽然isCancelled判断为YES,但是仍然往后执行了.

线程退出

+ (void)exit;
exit调用之后会立即终止线程,即使任务还没有执行完成也会中断。这就非常有可能导致内存泄露等严重问题,所以一般不推荐使用。 对于有runloop的线程,可以使用CFRunLoopStop()结束runloop配合-cancel结束线程

线程状态

executing是否正在执行,finished是否已经结束,cancelled是否已经取消了

@property (readonly, getter=isExecuting) BOOL executing API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));//线程是否正在执行
@property (readonly, getter=isFinished) BOOL finished API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));//线程是否已经结束
@property (readonly, getter=isCancelled) BOOL cancelled API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));//线程是否已经取消

主线程,当前线程,多线程判断或获取

isMainThread是否是主线程,mainThread获取主线程,class修饰的属性表示是一个类属性([NSThread isMainThread])
@property (readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@property (class, readonly) BOOL isMainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0)); // reports whether current thread is main
@property (class, readonly, strong) NSThread *mainThread API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
currentThread获取当前线程
@property (class, readonly, strong) NSThread *currentThread;
isMultiThreaded 是否是多线程
+ (BOOL)isMultiThreaded;

线程设置(名称,优先级)

name线程名称,主要用于debug快速定位对应出哪个线程出现的问题

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
threadPriority设置和获取线程优先级,范围0.0-1.0优先级越高越先执行,默认是0.5,iOS8之后新增了qualityOfService枚举属性,大家可以通过枚举值设置优先级 线程在创建后会放在线程调度池里,当开启线程时会根据设置的优先级去优先调哪个线程,所以设置优先级一点要在调start方法开启之前设置.
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;

@property double threadPriority API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); // To be deprecated; use qualityOfService below

@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); // read-only after the thread is started

优先级NSQualityOfService主要有5个枚举值,优先级别从高到低排布:

typedef NS_ENUM(NSInteger, NSQualityOfService) {
    NSQualityOfServiceUserInteractive = 0x21,
    NSQualityOfServiceUserInitiated = 0x19,
    NSQualityOfServiceDefault = -1,
    NSQualityOfServiceUtility = 0x11,
    NSQualityOfServiceBackground = 0x09
} API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0));
NSQualityOfServiceUserInteractive:最高优先级,主要用于提供交互UI的操作,比如处理点击事件,绘制图像到屏幕上 NSQualityOfServiceUserInitiated:次高优先级,主要用于执行需要立即返回的任务 NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级 NSQualityOfServiceUtility:普通优先级,主要用于不需要立即返回的任务 NSQualityOfServiceBackground:后台优先级,用于完全不紧急的任务

例子:

//线程优先级
- (void)threadPriority{
    NSThread * t1 = [[NSThread alloc]initWithBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    t1.threadPriority = 0.3;
    t1.name = @"t1";
    NSThread * t2 = [[NSThread alloc]initWithBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    t2.threadPriority = 0.6;
    t2.name = @"t2";
    NSThread * t3 = [[NSThread alloc]initWithBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    t3.threadPriority = 0.5;
    t3.name = @"t3";

    NSThread * t4 = [[NSThread alloc]initWithBlock:^{
        NSLog(@"%@",[NSThread currentThread]);
    }];
    t4.qualityOfService = NSQualityOfServiceUserInteractive;
    t4.name = @"t4";

    [t1 start];
    [t2 start];
    [t3 start];
    [t4 start];
}

线程通知

NSThread有三个线程相关的通知,

FOUNDATION_EXPORT NSNotificationName const NSWillBecomeMultiThreadedNotification;
FOUNDATION_EXPORT NSNotificationName const NSDidBecomeSingleThreadedNotification;
FOUNDATION_EXPORT NSNotificationName const NSThreadWillExitNotification;

NSWillBecomeMultiThreadedNotification:由当前线程派生出第一个其他线程时发送,一般一个线程只发送一次
NSDidBecomeSingleThreadedNotification:这个通知目前没有实际意义,可以忽略
NSThreadWillExitNotification线程退出之前发送这个通知

线程通讯

完成一些操作需要多线程会更加的有效,不同的线程做不同任务,互相有一些依赖,这就需要线程之间进行通讯.NSThread头文件中有一个NSObject的NSThreadPerformAdditions分类,只要继承NSObject的类都可以用里面的方法进行通讯,共5个方法:

#import "JThread.h"//自定义继承NSThread的线程

@implementation JThread
//结束线程的时候,我们可以使用CFRunLoopStop()配合-cancel来结束线程
-(void)cancelThread{
    [[NSThread currentThread] cancel];
    CFRunLoopStop(CFRunLoopGetCurrent());
}
@end

@interface NSObject (NSThreadPerformAdditions)

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

@end
performSelectorOnMainThread:将aSelector里的方法放到主线程中执行; performSelector:(SEL)aSelector onThread::将aSelector里的方法放到指定线程中执行; performSelectorInBackground:开启一个后台子线程,并执行aSelector里的方法. wait参数表示是否阻塞,YES是阻塞的,等线程中的方法执行完才会去执行对应线程中的方法,NO就是不阻塞,直接执行对应线程中的方法;如果本身是主线程有调用了performSelectorOnMainThread方法,wait参数是无效的,不论是设置为YES还是NO都会立即执行.

实例应用

@interface ViewController ()
@property(nonatomic ,copy) JThread * customThread;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.view.backgroundColor = [UIColor yellowColor];
    [self threadExample];
}

- (void)threadExample{
    [self.customThread start];//启动线程
    [self performSelector:@selector(handleTimeConsumingOperation) onThread:self.customThread withObject:nil waitUntilDone:NO];//线程间通讯
}
-(NSThread *)customThread
{
    if (!_customThread) {
        _customThread = [[JThread alloc]initWithTarget:self selector:@selector(threadTest:) object:@"创建一个新的线程"];//创建
        _customThread.name = @"customThread";//设置名称
        _customThread.qualityOfService = NSQualityOfServiceDefault;//设置优先级
    }
    return _customThread;
}
- (void)threadTest:(id)pram{
    NSRunLoop * runLoop = [NSRunLoop currentRunLoop];//给当前线程添加runLoop
    [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];//给runLoop添加数据源
    while (![[NSThread currentThread] isCancelled]) {
        [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];//启动runLoop;
    }
}
- (void)handleTimeConsumingOperation{

    for (int i = 0; i < 10000; i++) {
        NSLog(@"%@:%zd",[NSThread currentThread],i);
        if (i == 500) {
            [self.customThread cancelThread];
            NSLog(@"在当前线程中的方法取消线程,当前方法任然继续执行,但后面再添加方法到取消的线程中执行,则不会执行了");
            [self performSelector:@selector(test) onThread:self.customThread withObject:nil waitUntilDone:NO];//线程间通讯
            self.customThread = nil;
            break;
        }
    }
}
- (void)test{
    NSLog(@"不会再执行了");
}
@end

www.bkjia.comtruehttp://www.bkjia.com/qtjc/1304354.htmlTechArticle多线程-NSThread介绍,多线程-nsthread NSThread简介 NSThread封装性差,最偏向于底层,主要基于thread使用,OC语言,需要程序员自己管理生命周期. 每...

相关文章

帮客评论

视觉看点