多线程工具之NSThread

一个NSThread对象就是一个线程
 
1.创建线程
 
类存储在堆内存中,对象存储在栈内存中
 
/ / 是否是多线程
[NSThread isMultiThreaded]
//是否是主线程
[NSThread isMainThread]
//是否是当前线程
[NSThread currentThread]
 
开启新的线程的四种方法
//1.使用NSThread类方法➕detach方法,隐式创建  addAction12就是该线程的入口方法
     //注意: 每个线程都有main函数,这里相当于在main函数中self调用了addAction12方法
    //这种方式的缺点是非常的不灵活,没办法操作thread,开启时间点也没办法控制
    [NSThread detachNewThreadSelector:@selector(addAction12) toTarget:self withObject:nil];
//2.使用NSObject的线程扩展perform方法
      //隐式方法 与上一个方法使用类似
//    [self performSelectorInBackground:@selector(addAction12) withObject:nil];
//3.创建一个NSThread对象,然后调用start方法执行:
    //显式创建
    NSThread* thread = [[NSThread alloc]initWithTarget:self selector:@selector(addAction3) object:nil];
    thread.name = @"thread_name";
    
//配置线程栈空间
    //在线程开始之前 设置栈空间才有效,不能使用创建线程的第一和第二种方法
    thread.stackSize = 100;
    
//配置线程的本地存储
    //线程的全局数据 是readOnly
    [thread.threadDictionary setObject:@"value1" forKey:@"key1"];
   
    //错误写法,这是set方法,readOnly不能用
//    thread.threadDictionary = [[NSMutableDictionary alloc]init];
   
//线程的优先级
    //范围是0-1,最高是1,iOS8中更新时被qualityOfService代替
        / /根据线程的优先级来决定线程的执行顺序。
    thread.threadPriority = 1.0;
    thread.qualityOfService = NSQualityOfServiceUserInteractive;//不能再线程start后修改
   
//开启线程(现在才真正的创建出一个新的线程)
    [thread start];
//4.创建一个NSThread子类,然后实例化调用start方法
    //子类NSThread,重写main方法,,率先进入CNThread的main方法
         / /显示创建
    CNThread* cnThread = [[CNThread alloc]init];
   
    [cnThread start];
/ /在当前线程中调用
          / /就是viewDidLoad在哪个线程中,直接调用的方法就在哪个线程中
          [self addAction12];
          [self performSelect…..];
 
 
2.设置线程的Detached、Joinable状态
脱离线程(Detach Thread)---线程完成后,系统自动释放它所占用的内存空间
可连接线程(Joinable Thread)---一线程完成后,不回收可连接线程的资源
 
通过NSThread创建的线程都是Detached的。如果你想要创建可连接线程,唯一的办法 是使用 POSIX 线程。POSIX 默认创建的 线程是可连接的。通过 pthread_attr_setdetachstate函数设置是否脱离属性
 
3.完善线程的入口
 
1.autorelase
2.runloop
 
runloop可以长期循环运行,但我们已可以自己定义一个循环,让它保持长期运行,但是如果不用runloop,中间线程不会休息cpu损耗过大,要想让程序每隔3秒执行一次,就需要添加一个nstimer,但是如果没有循环timer一运行就会被杀死,所以必须有timer也必须有循环,但是循环进去后,又会一直卡死在循环里出不去,
- (void)viewDidLoad {
    [super viewDidLoad];
    NSThread* thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadAction) object:nil];
    [thread start];
    //如果直接在当前线程中调用循环,就会卡死在这,不会进入程序
//    while (true) {
//          [self action];
//    }
 
}
- (void)threadAction{
    @autoreleasepool {
        //新线程不会干扰到主线程
        [[NSThread currentThread].threadDictionary setObject:@(false) forKey:@"isEixt"];
        while (true) {
            if ([[[NSThread currentThread].threadDictionary valueForKey:@"isEixt"]boolValue]) {
                return;
            }
            //在新线程中调用
            [self action];
        }
    }
}
- (void)action{
    count++;
    if (count == 10000) {
        [[NSThread currentThread].threadDictionary setObject:@(true) forKey:@"isEixt"];
    }
    NSLog(@"-----,%d",count);
}
 
因上总结我们就得用到runloop了
主线程有一个runloop默认是开启的,但其他线程也有自己的一个runloop默认是关闭的,所以需要我们手动开启
 - (void)threadAction{
    @autoreleasepool {
        //新线程不会干扰到主线程
        [[NSThread currentThread].threadDictionary setObject:@(false) forKey:@"isEixt"];
 
        NSTimer* timer =  [NSTimer timerWithTimeInterval:3 target:self selector:@selector(action) userInfo:nil repeats:YES];
       
        //添加时间源
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        //开启loop方法
        [[NSRunLoop currentRunLoop]run];
 
}
//timer是run起来了,但是怎么终止呢,下面来讲解一下
 
4.终止线程
终止线程不要用POSIX直接杀死线程,会照成内存泄漏
最好的方式:让线程接收取消和退出消息
 
- (void)threadRoutine{
    @autoreleasepool {
       
                   //每一次的 NSRunloop循环都检查退出条件是否为YES,如果为YES退出循环回收资源,如果为NO,则 进入下一次NSRunloop 循环。
        BOOL exitNow = NO;
        //是否有更多的任务
        BOOL moreWorkToDo = YES;
        NSRunLoop* runLoop = [NSRunLoop currentRunLoop];
        NSMutableDictionary* threadDict = [[NSThread currentThread]
                                           threadDictionary];
        [threadDict setValue:[NSNumber numberWithBool:exitNow]
                      forKey:@"ThreadShouldExitNow"];
    
        NSTimer* timer =  [NSTimer timerWithTimeInterval:3 target:self selector:@selector(action) userInfo:nil repeats:YES];
        //添加时间源
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
 
        //终止线程
        while (moreWorkToDo && !exitNow)
        {
            //执⾏行线程真正的工作方法,如果完成了可以设置moreWorkToDo为False,就是不执行更多的任务了
            //打开了runloop,runloop又监听timer,当timer的action执行完以后,该语句才会执行完
            [runLoop runUntilDate:[NSDate date]];
 
            exitNow = [[threadDict valueForKey:@"ThreadShouldExitNow"]
                       boolValue];
        }
    }
}
- (void)action{
    count++;
    if (count == 3) {
        [[NSThread currentThread].threadDictionary setObject:@(true) forKey:@"ThreadShouldExitNow"];
    }
    NSLog(@"-----,%d",count);
}
//执行结果每隔3秒运行一次,执行三次后退出循环
runUntilDate:方法换为runMode:beforeDate:方法
 
//如果使用runUntilDate:,代码会一直进入循环里边出不来,一直调用action方法,无法进入runloop里面,要想进入runloop里面(循环不一直在循环里边),我们把方法改为了runMode:beforeDate:设置一个超时时间,使得代码不会马上就跑完,一直在跑,跑完之后就等待
//此时又出现了新的问题,进入了runloop后又出不来了,所以我们又加了CFRunLoopStop(CFRunLoopGetCurrent());使得runloop停止,停止后,就可以返回到 runMode:beforeDate:,while循环就可以继续工作了
  //一次Timer事件触发处理后, 这个RunLoop要想有返回值就要用runmode方法
  //如果在action中,没有停止RunLoop的操作的话,RunLoop 一直在运行,下面这行代码 永远不会有返回值
BOOL res = [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
NSLog(@"%d",res);
//    核心代码
    //这行代码写在timer的触发事件中
         / /runloop停止
    CFRunLoopStop(CFRunLoopGetCurrent());
 
这时候再在while循环中加入[self doOtherAction];此时就能实现交替循环了
 
[self doOtherAction];
- (void)doOtherAction{

   
NSLog(@"++++++");
}
//此循环实现了当主线程处于空闲时间时,可以执行其他线程
 
5.取消线程
 
子类化一个NSThread,在子类中重写main方法,使得线程率先进入main
-(void)main
{
    @autoreleasepool {
        NSLog(@"starting thread.......");
        NSTimer *timer = [NSTimer timerWithTimeInterval:1
                                                 target:self selector:@selector(doTimerTask) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:timer
                                    forMode:NSDefaultRunLoopMode];
        // 如果当前线程不被取消,则进入循环
        while (!self.isCancelled) {
            [self doOtherTask];
           
//           如果在doTimerTask中,没有停止RunLoop的操作的话,RunLoop 一直在运行,下面这行代码 永远不会有返回值
            //runloop开始执行
            BOOL ret = [[NSRunLoop currentRunLoop]
                        runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
       
            NSLog(@"after runloop counting.........: %d", ret);
        }
        
        NSLog(@"finishing thread.........");
    }
}
- (void)doTimerTask
{
    NSLog(@"do timer task");
    
//    添加RunLoop停止代码,使NSRunLoop runMode:(NSString *)mode beforeDate:(NSDate *)limitDate方法返回
 / /停止代码必须在runloop运行接口为 runMode:下才能用
//    这行代码写在 timer的触发事件中
    CFRunLoopStop(CFRunLoopGetCurrent());
    
}
- (void)doOtherTask
{
    NSLog(@"do other task");
}
 
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        //4秒过后取消线程
        [thread cancel];
    });
 
效果图对比如下:
 
图1:当你不添加runloop停止代码时,进入runloop的代码出不来,会一直执行timer的doTimerAction方法。。。
图2:添加runloop停止代码后,停止后,就可以返回到runMode:beforeDate:,while循环就可以继续工作了
图3:加入线程取消代码,该线程就会延迟4秒后取消
 
6.runloop的运行接口
 
//运⾏ NSRunLoop,运⾏行模式为默认的NSDefaultRunLoopMode模式,运行起来就永远不会停止
- (void)run;
//运⾏NSRunLoop: 参数为运行时间期限,运⾏行模式为默认的NSDefaultRunLoopMode模式
-(void)runUntilDate:(NSDate *)limitDate;
//运⾏NSRunLoop: 参数为 运⾏行模式时间期限,返回值为YES(1)表⽰示是处理事件后返回的,NO(0)表⽰示是超时或者停⽌止运⾏行导致返回的
- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate;
 
7.线程同步
在多个线程访问相同的数据时,有可能会造成数据的冲突。比如常见的售票问题。
 
/ /首先创建3个线程,每个线程都调用sellticket方法,初始值ticket为100;
//卖票
- (void)sellTicket{
     //获取当前的票数
    int current = _ticket;
    //若当前票数为0,说明票已经卖完,跳出循环
    if (current == 0) {
        NSLog(@"ticket:%d,sold:%d",_ticket,_sold);
        return;
    }
    //买票延时
    usleep(10000);
    //每循环一次,票数久等于当前票数减一
    _ticket = current- 1;
    NSLog(@"---%@--,%d",[[NSThread currentThread]name],_ticket);
    //每循环一次,卖出的票数就加一
    _sold++;
    //递归调用
    [self sellTicket];
   
}
效果如图
显而易见这个程序是有问题的,每张票在三个窗口都会卖一次,最后卖的票数会多出总票数,要想解决该问题,就要用到数据同步锁
 
8.数据同步锁(NSLock)
 
加锁前需要先初始化
//加锁
[_lock lock];
//解锁
[_lock unlock];
 
9.数据等待(NSCondition )
 
A线程需要等待B线程执行后的某个结果继续执行,也就是同步 问题,这时就会需要A等待B,解决方式如下:
 
- (void)viewDidLoad {
    [super viewDidLoad];
    _lock = [[NSCondition alloc]init];
   
    [self performSelectorInBackground:@selector(cook) withObject:nil];
    [self performSelector:@selector(buyStuff) withObject:nil afterDelay:4];
}
- (void)cook{
    [[NSThread currentThread]setName:@"cook_thread"];
    
    NSLog(@"开始做饭");
    NSLog(@"发现没菜,需要去买菜");
   
    [_lock lock];
    [_lock wait];
    usleep(1000000);
    NSLog(@"买菜回来,开始做饭");
}
- (void)buyStuff{
    NSLog(@"买菜进行中。。。。");
    //发送信号
    [_lock signal];
}
 
10.nonatomic和atomic
在@property中最直观的用法就是生成set和get方法
readOnly 只有get方法
atomic:默认是有该属性的,这个属性是为了保证程序在多线程情况下,编译器会自动生成一 些互斥加锁代码,避免该变量的读写不同步问题。
nonatomic:如果该对象无需考虑多线程的 情况,请加入这个属性,这样会让编译器少生成一些互斥加锁代码,可以提高效率。
 

转载于:https://www.cnblogs.com/gaominmin/p/4822836.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值