前面总结了多线程基本概念和iOS多线程PThread的使用,下面接着总结iOS多线程的另外一种实现方案NSThread。
一、基本概念
NSThread是苹果封装的面向对象的多线程实现技术方案, 我们可以直观方便地操控线程对象,但需要自己控制线程的生命周期。
一、NSThread的使用
1、创建线程
#pragma mark - NSThread
- (void)nsTheadMethod {
NSLog(@"主线程执行");
// 创建线程方法
// 1. 通过alloc init方式创建执行线程
NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(runNSThreadMethod) object:nil];
[thread1 start];
// 2. 通过detachNewThreadSelector方式创建并执行线程
[NSThread detachNewThreadSelector:@selector(runNSThreadMethod) toTarget:self withObject:nil];
// 3. 通过performSelectorInBackground(NSObject的方法)方式创建并执行线程
[self performSelectorInBackground:@selector(runNSThreadMethod) withObject:nil];
}
#pragma mark - 线程执行方法
- (void)runNSThreadMethod {
NSLog(@"子线程执行");
for (NSInteger index = 0; index <= 5; index ++) {
NSLog(@"%ld", index);
sleep(1);
if (index == 4) {
[self performSelectorOnMainThread:@selector(runOnMainThreadMethod) withObject:nil waitUntilDone:YES];
}
}
}
#pragma mark - 主线程执行方法
- (void)runOnMainThreadMethod {
NSLog(@"回到主线程执行!!!");
}
- 创建线程有上述三种方式;
- 方法1需要需要创建
NSThread
对象,并调用start
方法执行; - 方法2和方法3均不需要创建对象和单独调用方法;
- 在需要设置线程的属性方法时需要用方法1来创建线程对象;
-
performSelectorInBackground
和performSelectorOnMainThread
均为NSObject的方法,可直接用self调用。
2、线程的属性方法
- 设置线程名字
[thread1 setName:@"Thread_1"];
- 获取当前线程的线程对象
[NSThread currentThread]
- 获取当前线程名字
[NSThread currentThread].name
- 线程优先级
setThreadPriority
,数据多时可验证优先级高的线程执行可能性更高。 - 休眠
[NSThread sleepForTimeInterval:3]
3、NSThread锁的使用
当不同的线程同时去改变一个值时,会发生紊乱,此时需要引入线程锁。
下面模拟一个售票系统:
#import "TicketManager.h"
#define KTotalTickets 50
@interface TicketManager ()
@property (nonatomic, assign) NSInteger remainTicket; // 余票
@property (nonatomic, assign) NSInteger saleCount; // 卖出票数
@property (nonatomic, strong) NSThread *threadCD; // 成都
@property (nonatomic, strong) NSThread *threadSZ; // 深圳
@end
@implementation TicketManager
- (instancetype)init {
self = [super init];
if (self) {
self.remainTicket = KTotalTickets;
// 定义两个线程
self.threadCD = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketMethod) object:nil];
[self.threadCD setName:@"成都_Thread"]; // 设置线程名字
self.threadSZ = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicketMethod) object:nil];
[self.threadSZ setName:@"深圳_Thread"];
}
return self;
}
#pragma mark - 卖票
- (void)saleTicketMethod {
while (true) {
// 只要remainTickets大于0说明还有余票可卖
if (self.remainTicket > 0) {
// 睡眠
[NSThread sleepForTimeInterval:0.5];
// 余票减
self.remainTicket --;
// 修改卖票
self.saleCount = KTotalTickets - self.remainTicket;
NSLog(@"%@:当前余票: %ld, 售出:%ld", [NSThread currentThread].name, self.remainTicket, self.saleCount);
}
}
}
#pragma mark - 启动售票
- (void)startToSale {
[self.threadCD start];
[self.threadSZ start];
}
@end
调用:
TicketManager *ticketManager = [[TicketManager alloc]init];
[ticketManager startToSale];
部分运行结果:
2019-04-11 00:07:06.334705+0800 Test[73998:344573] 深圳_Thread:当前余票: 49, 售出:1
2019-04-11 00:07:06.334705+0800 Test[73998:344572] 成都_Thread:当前余票: 49, 售出:1
2019-04-11 00:07:06.837866+0800 Test[73998:344573] 深圳_Thread:当前余票: 48, 售出:2
2019-04-11 00:07:06.837873+0800 Test[73998:344572] 成都_Thread:当前余票: 47, 售出:3
2019-04-11 00:07:07.341862+0800 Test[73998:344572] 成都_Thread:当前余票: 45, 售出:5
2019-04-11 00:07:07.341862+0800 Test[73998:344573] 深圳_Thread:当前余票: 46, 售出:4
2019-04-11 00:07:07.844193+0800 Test[73998:344573] 深圳_Thread:当前余票: 43, 售出:7
2019-04-11 00:07:07.844193+0800 Test[73998:344572] 成都_Thread:当前余票: 44, 售出:6
2019-04-11 00:07:08.344630+0800 Test[73998:344572] 成都_Thread:当前余票: 41, 售出:9
2019-04-11 00:07:08.344630+0800 Test[73998:344573] 深圳_Thread:当前余票: 42, 售出:8
2019-04-11 00:07:08.849986+0800 Test[73998:344573] 深圳_Thread:当前余票: 40, 售出:10
2019-04-11 00:07:08.849987+0800 Test[73998:344572] 成都_Thread:当前余票: 39, 售出:11
2019-04-11 00:07:09.353644+0800 Test[73998:344572] 成都_Thread:当前余票: 37, 售出:13
2019-04-11 00:07:09.353648+0800 Test[73998:344573] 深圳_Thread:当前余票: 38, 售出:12
2019-04-11 00:07:09.855473+0800 Test[73998:344573] 深圳_Thread:当前余票: 36, 售出:14
2019-04-11 00:07:09.855473+0800 Test[73998:344572] 成都_Thread:当前余票: 36, 售出:14
如上所示,有些地方同时出现了相同余票并可卖,很明显是有问题的,所以需要加锁,在一个线程操作某个数据时,其他线程不允许操作该数据。
- 方法1:
@synchronized
加锁
#pragma mark - 卖票
- (void)saleTicketMethod {
while (true) {
@synchronized (self) {
// 只要remainTickets大于0说明还有余票可卖
if (self.remainTicket > 0) {
// 睡眠
[NSThread sleepForTimeInterval:0.5];
// 余票减
self.remainTicket --;
// 修改卖票
self.saleCount = KTotalTickets - self.remainTicket;
NSLog(@"%@:当前余票: %ld, 售出:%ld", [NSThread currentThread].name, self.remainTicket, self.saleCount);
}
}
}
}
- 方法2:
NSCondition
加锁
// 定义
@property (nonatomic, strong) NSCondition *ticketCondition;
// 初始化
self.ticketCondition = [[NSCondition alloc]init];
// 加锁 解锁
#pragma mark - 卖票
- (void)saleTicketMethod {
while (true) {
// 加锁
[self.ticketCondition lock];
// 只要remainTickets大于0说明还有余票可卖
if (self.remainTicket > 0) {
// 睡眠
[NSThread sleepForTimeInterval:0.5];
// 余票减
self.remainTicket --;
// 修改卖票
self.saleCount = KTotalTickets - self.remainTicket;
NSLog(@"%@:当前余票: %ld, 售出:%ld", [NSThread currentThread].name, self.remainTicket, self.saleCount);
}
// 执行完成 解锁
[self.ticketCondition unlock];
}
}
- NSLock等其他加锁方式。