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,有问题请在问候留言