ios 线程之 NSThread

NSThread

使用NSThread有多种方式:

  1. 第一种直接alloc创建线程,需要手动调用start启动线程
/*
     第一个参数:目标对象
     第二个参数:方法选择器,调用的方法
     第三个参数:前面调用的方法需要传递的参数,可不传
     */
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(cycleLog:) object:@"acb"];
    //启动线程
    [thread start];
  1. 通过类方法创建
[NSThread detachNewThreadSelector:@selector(cycleLog:) toTarget:self withObject:@"dfdf"];
    //使用block方式
    [NSThread detachNewThreadWithBlock:^{
        [self cycleLog:@"dsfds"];
    }];

3.通过使用performSelectorInBackground开启后台线程执行任务

[self performSelectorInBackground:@selector(cycleLog:) withObject:@"bacground"];
NSThread相关重要属性及方法
  • name线程名称用于区分不同线程
  • threadPriority设置线程优先级取值范围0-1,1优先级最高,默认优先级为0.5
  • +(double)threadPriority获取线程优先级
  • + (BOOL)setThreadPriority:(double)p设置线程优先级,优先级越大则cpu调度的机会越大
 NSThread *threada = [[NSThread alloc]initWithTarget:self selector:@selector(cycleLog:) object:@"acb"];
    threada.name=@"A线程";
    //取值范围0-1,1优先级最高
    threada.threadPriority=0.5;

创建NSThred线程三种方式总结
  1. 创建NSThread对象后,手动启动线程
  2. 通过NSThread类方法detachNewThreadSelector创建线程后自动启动线程
  3. 通过NSObject的NSThread分类方法performSelectorInBackground隐式创建并启动线程。

上述2、3两种创建线程方式的优缺点;
优点:简单快捷
缺点:没法拿到线程对象没法设置线程名称和线程优先级等详细设置

NSThread 的生命周期。

前面章节说过,NSThread是半自动管理,程序员创建,系统销毁的。那是什么时候销毁的呢?
通常在方法中创建一个对象,没有外部的强引用的话,方法运行完成则改对象将被销毁。而NSThread销毁时机为任务执行完毕后。如下示例

@interface TDThread : NSThread
@end

@implementation TDThread
-(void)dealloc{
    NSLog(@"%@--%s",[self class],__func__);
}
@end
-(void)threadLifeCycle{
    TDThread *thread = [[TDThread alloc]initWithTarget:self selector:@selector(cycleLog:) object:nil];
    [thread start];
    
}
-(void)cycleLog:(NSString *)param{
    
    NSThread *thread=[NSThread currentThread];
    for (int i=0; i<1000; i++) {
        NSLog(@"index = %d,  thread--%@",i,thread);
    }
}

在这里插入图片描述
通过日志打印可看出线程是任务执行完成后销毁的。

线程的状态

线程的状态有,新建、就绪、运行、挂起、死亡;各状态将关系如下

start
CPU调度当前线程
调用了sleep/等待同步锁
线程任务执行完毕\异常\强制退出
CPU调用其他线程
sleep到时/得到同步锁
新建
new
就绪
Runnable
运行
Running
阻塞
blocked
死忘
Dead

创建NSThread对象的时候,thread是新建状态,调用start方法时,系统将其加入可调度线程池中,thread变为就绪状态,但cpu调度该线程时,thread变为运行状态,前面说过线程运行是cpu快速切换调度的(单核)当cpu调用其他线程时,改thread变为就绪状态,再次进入可调度线程池中,如果线程调用sleep或等待同步锁时线程进入阻塞状态,thread被移除可调度线程池,单sleep到时,或已获得同步锁时,thread再次进入到就绪转态,再次被加入可调度线程池中。如果thread任务执行完毕或遇到异常或调用了强制退出是线程进入死亡转态;
注意:一点线程进入停止(死亡状态)了,就不能再次开启任务

线程安全

当线程之间资源共享时是有安全风险的

  • 资源共享
  1. 一块资源可能被多个线程共享,也就是多个线程可能会访问同一块资源;比如多个线程访问同一个对象,同一个变量、同一个文件等;
    当多个线程访问同一块资源是,很容易引发数据错乱和数据安全问题;
    如下图为apple提供是示例图:
    在这里插入图片描述
    两个线程threadA、threadB都读取数据17进行操作引起的线程安全问题。
    当threadA读取数据17时进行加操作,threadB也同时读取了数据17进行加操作。thread将加后的数据赋回原变量时为18,而threadA也完成操作时赋值回去时数据还是18,做了两次加操作还是18是不对的。苹果给出了一种解决方案,加互斥锁:
    在这里插入图片描述
    threadA读取数据17时对其加锁,如果同时threadB也来读取数据17时,看到有锁则挂起等待锁的释放,ThreadA执行完成后释放同步锁,这时ThreadB对数据读取并加锁这时读取到的数据未18ThreadB完成执行后最终数据未19

卖票例子如下:

-(void)threadSafeThread{
    self.ticketCount = 100;
    NSThread *threadA=[[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    threadA.name = @"窗口1";
    NSThread *threadB=[[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    threadB.name = @"窗口2";
    NSThread *threadC=[[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
    threadC.name = @"窗口3";
    [threadA start];
    [threadB start];
    [threadC start];
}
-(void)saleTicket{
//锁不能加在该位置,如果在该位置则一个窗口全部卖完。可以试试看效果
    while (1) {
        //锁:必须全局唯一
        @synchronized (self) {
            NSInteger count = self.ticketCount;
            if (count>0) {
                //耗时操作
                for (NSInteger i=0; i<10000; i++) {
                    
                }
                self.ticketCount =count -1;
                
    //            for (int i=0; i<100; i++) {
    //
    //            }
                NSLog(@"%@卖了一张票,还剩%zd张票",[NSThread currentThread].name,self.ticketCount);
            }else{
                NSLog(@"票卖完了");
                
                break;//任务执行完成退出。区别于[NSThread exit]强制退出程序
            }
        }
        
    }
}

注意:设置锁对象必须全局唯一,通常直接将当前对象self
注意点:

  1. 注意加锁位置
  2. 注意加锁的前提条件,多线程共享同一块资源
  3. 注意加锁是需要代价的,需要耗费性能
  4. 加锁的结果:照成线程同步
  • 互斥锁使用格式
    @synchronized (锁对象){//需要锁定的代码}注意:锁定一份代码只用一把锁,用多把锁是无效的
互斥锁的优缺点
  1. 有点:能有效防止因多线程抢夺资源造成的数据安全问题
  2. 确定:需要消耗大量cpu资源
  • 互斥锁使用前提:多条线程访问统一块资源
  • 线程同步:
    线程同步的意思是,多条线程在同一条线上执行(按顺序的执行任务)

ios原子和非原子属性的选择
NSThread实现线程间通信
GCD简介

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值