美文网首页
iOS多线程之NSThread

iOS多线程之NSThread

作者: YANGXIXIYear | 来源:发表于2019-04-10 23:54 被阅读0次

前面总结了多线程基本概念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来创建线程对象;
  • performSelectorInBackgroundperformSelectorOnMainThread均为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等其他加锁方式。

相关文章

网友评论

      本文标题:iOS多线程之NSThread

      本文链接:https://www.haomeiwen.com/subject/dbiaiqtx.html