iOS多线程-NSthread

2018-08-23  本文已影响8人  rainbowboy

祭出demo
1、类方法及属性

/*
类属性,调用这个方法时,返回的是当前执行的线程
*/
@property (class, readonly, strong) NSThread *currentThread;

/*
该类方法会启动一个线程,并且启动线程,无需调用start。
*/
+ (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

/*
该类方法会启动一个线程,并且启动线程,无需调用start。
*/
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
/*设置当前线程sleep到指定时间启动*/
+ (void)sleepUntilDate:(NSDate *)date;
/*设置当前线程sleep到指定ti秒后启动*/
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
/*设置当前线程退出,退出的地方不会执行exit后面的代码*/
+ (void)exit;
/*获取当前线程的优先级*/
+ (double)threadPriority;
/*设置当前线程的优先级,并且返回成功与失败状态*/
+ (BOOL)setThreadPriority:(double)p;

2、实例方法与属性

/*设置线程优先级,已经被弃用,iOS8.0后建议使用qualityOfService*/
@property double threadPriority API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0)); // To be deprecated; use qualityOfService below
/*设置线程优先级,iOS8.0后新增的属性*/
@property NSQualityOfService qualityOfService API_AVAILABLE(macos(10.10), ios(8.0), watchos(2.0), tvos(9.0)); // read-only after the thread is started

/*
使用target对象的selector作为线程的任务执行体,该selector方法最多可以接收一个参数,该参数即为argument
*/
- (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));

/*
使用block作为线程的任务执行体
*/
- (instancetype)initWithBlock:(void (^)(void))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

//判断线程是否正在执行
@property (readonly, getter=isExecuting) BOOL executing;

//判断线程是否结束
@property (readonly, getter=isFinished) BOOL finished;

//判断线程是否被取消
@property (readonly, getter=isCancelled) BOOL cancelled;

/*取消线程,调用后,cancelled会置为YES,但是线程不会真取消,调用exit之后才会终止*/
- (void)cancel API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
/*启动线程*/
- (void)start API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

3、简单事例

//实例
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMehod:) object:@"hi baby"];
    [thread setName:@"level low"];
thread.qualityOfService = NSQualityOfServiceBackground;
[thread start];

//类
[NSThread detachNewThreadSelector:@selector(threadMehod:) toTarget:self withObject:@"level"];


- (void)threadMehod:(NSString *)argument {
    
    NSLog(@"threadDictionary=%@",[[NSThread currentThread] threadDictionary]);
    
    for (NSInteger i = 0; i < 20; i++) {
        NSLog(@"thread:%@==%@ level=%lX",[NSThread currentThread],argument,(long)[NSThread currentThread].qualityOfService);
        [NSThread sleepForTimeInterval:0.1];
    }
    NSLog(@"thread task complete");
    
}

4、NSThread的锁机制
多线程会涉及到竞争条件,可以通过同步机制锁机制来解决.
来看看银行取钱的例子
无处理状态:

- (void)drawMoney:(NSNumber *)drawCash {
    
    if (self.balance > drawCash.integerValue) {
        [NSThread sleepForTimeInterval:0.01];
        self.balance -= drawCash.integerValue;
        NSLog(@"leave money is %ld in threadName:%@",self.balance,[NSThread currentThread].name);
        
    }else{
        
        NSLog(@"There is not enough money in threadName:%@",[NSThread currentThread].name);
        
    }
    
}

加锁处理:

- (void)drawMoneyWithLock:(NSNumber *)drawCash {
    [self.lock lock];
    if (self.balance > drawCash.integerValue) {
        [NSThread sleepForTimeInterval:0.01];
        self.balance -= drawCash.integerValue;
        NSLog(@"leave money is %ld in threadName:%@",self.balance,[NSThread currentThread].name);
        
    }else{
        
        NSLog(@"There is not enough money in threadName:%@",[NSThread currentThread].name);
        
    }
    [self.lock unlock];
}

多个线程碰到lock在使用中时就会等待lock释放,等lock释放时,系统就会调度一个阻塞的线程来取钱了

同步代码块

- (void)drawMoneyWithSynchronize:(NSNumber *)drawCash {
    @synchronized(self){
        if (self.balance > drawCash.integerValue) {
            [NSThread sleepForTimeInterval:0.01];
            self.balance -= drawCash.integerValue;
            NSLog(@"leave money is %ld in threadName:%@",self.balance,[NSThread currentThread].name);
            
        }else{
            
            NSLog(@"There is not enough money in threadName:%@",[NSThread currentThread].name);
            
        }
    }
    
}

@synchronized实现了大括号里的同步代码块,同时监听BlankAccount,其他线程需要获取到监听的BlankAccount才能被调度。

5、NSCondition有条件的多线程调度

NS_CLASS_AVAILABLE(10_5, 2_0)
@interface NSCondition : NSObject <NSLocking> {
@private
    void *_priv;
}

/*
调用NSCondition对象wait方法的线程会阻塞,直到其他线程调用该对象的signal方法或broadcast方法来唤醒
唤醒后该线程从阻塞态改为就绪态,交由系统进行线程调度
执行wait方法时内部会自动执行unlock方法释放锁,并阻塞线程
*/
- (void)wait;

//同上,只是该方法是在limit到达时唤醒线程
- (BOOL)waitUntilDate:(NSDate *)limit;

/*
唤醒在当前NSCondition对象上阻塞的一个线程
如果在该对象上wait的有多个线程则随机挑选一个,被挑选的线程则从阻塞态进入就绪态
*/
- (void)signal;

/*
同上,该方法会唤醒在当前NSCondition对象上阻塞的所有线程
*/
- (void)broadcast;

@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

@end

NSCondition使用示例:

@interface BankAccount : NSObject
@property(nonatomic, strong)NSString *name;
@property(nonatomic, assign)NSInteger balance;
/**
 condition的取钱

 @param drawCash <#drawCash description#>
 */
- (void)drawMoneyWithCondition:(NSNumber *)drawCash;

/**
  condition的存钱

 @param depositeCash <#depositeCash description#>
 */
- (void)depositeMoneyWithCondition:(NSNumber *)depositeCash;

@end

@interface BankAccount()
@property (nonatomic, strong)NSCondition *condition;
@property (nonatomic, assign) BOOL haveMoney;
@end

@implementation BankAccount
- (NSCondition *)condition {
    @synchronized(self){
        /*
         这里一定要做同步操作,否则有概率导致被阻塞的线程无法被调起,可能是系统bug
         */
        if (_condition == nil) {
            _condition = [[NSCondition alloc]init];
        }
    }
    return _condition;
}

- (void)drawMoneyWithCondition:(NSNumber *)drawCash {
    //每个线程取钱20次
    
    NSInteger count = 0;
    while (count < 20) {
        //首先使用condition上锁,如果其他线程已经上锁则阻塞
        [self.condition lock];
            if (self.haveMoney) {
                self.balance -= drawCash.integerValue;
                self.haveMoney = NO;
                count++;
                NSLog(@"balance is %ld in threadName:%@ %ld",self.balance,[NSThread currentThread].name,count);
                //取钱操作完成后唤醒其他在此condition上等待的线程
                [self.condition broadcast];
            }else {
                //如果没有钱则在此condition上等待,并阻塞
                NSLog(@"draw-> threadName:%@ 阻塞",[NSThread currentThread].name);
                [self.condition wait];
                NSLog(@"draw-> threadName:%@ 启动",[NSThread currentThread].name);
            }
        [self.condition unlock];
    }

}

- (void)depositeMoneyWithCondition:(NSNumber *)depositeCash {
    //通过2个线程,每次取钱20次,存钱40次
    NSInteger count = 0;
    while (count < 40) {
        
        [self.condition lock];
            if (self.haveMoney == NO) {
                self.balance += depositeCash.integerValue;
                self.haveMoney = YES;
                count++;
                NSLog(@"balance is %ld in threadName:%@ %ld",self.balance,[NSThread currentThread].name,count);
                //取钱操作完成后唤醒其他在此condition上等待的线程
                [self.condition broadcast];
            }else {
                
                NSLog(@"deposite-> threadName:%@ 阻塞",[NSThread currentThread].name);
                
                [self.condition wait];
                
                NSLog(@"deposite-> threadName:%@ 启动",[NSThread currentThread].name);
                
            }
        [self.condition unlock];
    }
    
}

@end

线程调度:

- (void)viewDidLoad {
    [super viewDidLoad];
BankAccount *accountTwo = [[BankAccount alloc]init];
    accountTwo.name = @"王子银行";
    accountTwo.balance  = 0;
    NSThread *thread5 = [[NSThread alloc]initWithTarget:accountTwo selector:@selector(drawMoneyWithCondition:) object:@(500)];
    [thread5 setName:@"取钱者:猪八戒"];
    NSThread *thread6 = [[NSThread alloc]initWithTarget:accountTwo selector:@selector(drawMoneyWithCondition:) object:@(500)];
    [thread6 setName:@"取钱者:沙僧"];
    NSThread *thread7 = [[NSThread alloc]initWithTarget:accountTwo selector:@selector(depositeMoneyWithCondition:) object:@(500)];
    [thread7 setName:@"存钱者:孙悟空"];
    
    [thread5 start];
    [thread6 start];
    [thread7 start];
}

从控制台的log可以看出,孙悟空存钱了40次,猪八戒和沙僧分别取钱20次。

如有不清楚的地方请下载demo,有问题请在问候留言

祭出demo
更新时间: 2018-08-23

上一篇下一篇

猜你喜欢

热点阅读