iOS 多线程(二)NSThread

来源:互联网 发布:中国8月进出口数据库 编辑:程序博客网 时间:2024/06/09 12:13

iOS 使用NSThread来代表线程,创建新线程也就是创建一个NSThread对象。

1 创建和启动线程

iOS10之前提供了两种方法开启线程。

- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
第一种方法是实例方法,返回一个NSThread对象,必须调用start方法来启动线程。

第二种方法是类方法,不会返回NSThread对象,直接创建并启动线程。

iOS10增加了两种创建启动线程的方法

- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
这两个方法与上述类似,也是一个实例方法,一个类方法。只是执行方法体不一样了。
具体使用如下:

#import "ZPYViewController.h"@interface ZPYViewController ()@end@implementation ZPYViewController- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.        [self makeThread1beforeIOS10];    [self makeThread2beforeIOS10];    [self makeThread1FromIOS10];    [self makeThread2FromIOS10];}-(void)run{    for(int i=0;i<10;i++){        NSLog(@"%@,----i=%d",[NSThread currentThread],i);        NSLog(@"isMainThread=%d",[NSThread isMainThread]);    }}//iOS 10 之前NSThread两种方法创建线程。如下:-(void) makeThread1beforeIOS10{    //第一种创建方式 实例方法 最多可以传一个参数    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];    [thread1 setName:@"fisrt blood"];    //启动线程    [thread1 start];    BOOL isMain = [NSThread isMainThread];    NSLog(@"isMain=%d,thread1=%d",isMain,[thread1 isMainThread]);}//第二种创建方式 类方法。 最多可以传一个参数-(void) makeThread2beforeIOS10{    //类方法,不会返回NSThread对象,直接启动线程    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];}//iOS 10又添加了两个方法创建线程。// 需要iOS 10 才可以运行下面两个方法。否则会出错。//第三种创建方式 实例方法-(void) makeThread1FromIOS10{    NSThread *thread3 = [[NSThread alloc] initWithBlock:^{        for(int i=0;i<10;i++){            NSLog(@"%@,第三种 i=%d",[NSThread currentThread],i);        }    }];    thread3.name = @"triple kill";    //调用start方法启动线程    [thread3 start];}//第四种创建方式 类方法-(void) makeThread2FromIOS10{    //类方法,不会返回NSThread对象,直接启动线程    [NSThread detachNewThreadWithBlock:^{        for(int i=0;i<10;i++){            NSLog(@"%@,第四种 i=%d",[NSThread currentThread],i);        }    }];}@end

2 常用方法

主线程相关:

+ (NSThread *)mainThread; // 获得主线程- (BOOL)isMainThread; // 是否为主线程+ (BOOL)isMainThread; // 是否为主线程
其他方法:

获得当前线程

NSThread *current = [NSThreadcurrentThread];

线程的调度优先级

+ (double)threadPriority;

+ (BOOL)setThreadPriority:(double)p;

- (double)threadPriority;

- (BOOL)setThreadPriority:(double)p;

调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高


自己开发时,建议一般不要修改优先级

线程的名字

- (void)setName:(NSString *)n;

- (NSString *)name;


3 线程的状态

NSThread *thread1 = [[NSThreadalloc]initWithTarget:selfselector:@selector(run)object:nil];

//启动线程

[thread1 start];


五种状态:新建、就绪、运行、阻塞、死亡

新建:当程序建立一个线程后,该线程就处于新建状态,此时它和其他OC对象一样,仅仅有系统分配了内存,初始化了成员变量。

就绪:当线程对象调用start方法后,该线程处于就绪状态。处于该状态的线程并没有开始运行,至于何时运行,取决于系统的调度。

运行:当系统调度到该线程时,进入运行状态。调用到其他线程则该线程就又重新回到就绪状态。

阻塞:当线程调用了sleep方法或等待同步锁时进入阻塞状态。

死亡:当线程执行完毕,或异常/强制退出,则该线程进入死亡状态。

启动线程

- (void)start; //进入就绪状态-> 运行状态。当线程任务执行完毕,自动进入死亡状态

阻塞(暂停)线程

+ (void)sleepUntilDate:(NSDate *)date;

+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

// 进入阻塞状态


强制停止线程

+ (void)exit;

// 进入死亡状态

注意:一旦线程停止(死亡)了,就不能再次开启任务


4 停止线程

有时我们需要将子线程结束,这该怎么办呢?

线程结束方式又三种

1 线程执行体方法执行完成,线程正常结束。

2 线程执行过程中发生了异常。

3 调用NSThread的exit方法类停止当前正在执行的线程。

由于exit方法为类方法,所以只能停止正在执行的线程。

我们可以在主线程或者其他线程中调用[thread cancel]。而子线程的执行体中可以判断是否cancel来停止线程。

//结束线程    if([[NSThread currentThread] isCancelled]){        [NSThread exit];    }

5 多线程安全

在多线程中,必然会涉及到线程安全点问题,比如资源共享(多个线程访问同一个对象、同一个变量、同一个文件),这时很容易发生数据错乱、数据安全问题。

如下经典问题,取钱


如上图:1500取出1600,还剩余700,这样显然是不合理的(虽然对于用户来说很乐意了)。

-(void)takeMoney:(NSNumber *)count{    [_account take:count.doubleValue];}- (IBAction)actionTest:(id)sender {    _account = [[ZPYAccount alloc] initWithCardId:@"1234567" andBalance:1500];    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(takeMoney:) object:[NSNumber numberWithInt:800]];    [thread1 setName:@"first thread"];    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(takeMoney:) object:[NSNumber numberWithInt:800]];    [thread2 setName:@"second thread"];    [thread1 start];    [thread2 start];}
两个线程去做取钱的操作。

ZPYAccount中的take方法:

-(void)take:(double)count{    double money = _balance;    if(count <= money){        [NSThread sleepForTimeInterval:0.001];        _balance = money - count;        NSLog(@"thread:%@,取钱成功,余额为%f",[[NSThread currentThread] name],_balance);        }else{            NSLog(@"thread:%@,余额不足!",[[NSThread currentThread] name]);        }}
输出如下:
2016-10-11 11:40:09.052 NSThread使用[553:386755] thread:second thread,取钱成功,余额为700.0000002016-10-11 11:40:09.051 NSThread使用[553:386754] thread:first thread,取钱成功,余额为700.000000

1 @synchronized实现同步:

-(void)take:(double)count{    @synchronized (self) {        //将需要同时执行的放入同步代码块中。查钱、取钱。        double money = _balance;        if(count <= money){            [NSThread sleepForTimeInterval:0.001];            _balance = money - count;            NSLog(@"thread:%@,取钱成功,余额为%f",[[NSThread currentThread] name],_balance);        }else{            NSLog(@"thread:%@,余额不足!",[[NSThread currentThread] name]);        }    }}
打印:

2016-10-11 18:45:52.135 NSThread使用[556:387839] thread:first thread,取钱成功,余额为700.0000002016-10-11 18:45:52.137 NSThread使用[556:387840] thread:second thread,余额不足!

这样就合理了,当余额不足时就不再让取钱,并且余额也没有问题。

2 NSLock 同步锁

使用NSLock也可以保证线程安全。

_lock = [[NSLockalloc]init];

-(void)takelock:(double)count{    [_lock lock];    double money = _balance;    if(count <= money){        [NSThread sleepForTimeInterval:0.001];        _balance = money - count;        NSLog(@"thread:%@,取钱成功,余额为%f",[[NSThread currentThread] name],_balance);    }else{        NSLog(@"thread:%@,余额不足!",[[NSThread currentThread] name]);    }    [_lock unlock];}
打印:

2016-10-11 18:52:09.216 NSThread使用[559:388860] thread:first thread,取钱成功,余额为700.0000002016-10-11 18:52:09.217 NSThread使用[559:388861] thread:second thread,余额不足!


0 0
原创粉丝点击