iOS - 多线程三部曲之NSThread(一)

2018-10-09  本文已影响160人  flowerflower
😀😀

目录
一、基础理论
二、结合案例使用

前言
多线程这个话题在网上搜的话直接一堆堆的,在这里我也就不瞎比比了,直接上正菜靠谱、实在一点。所谓上正菜就是在实际项目用到的,不是谈一些子虚乌有的东西。


一、基础理论

NSThread 是苹果官方提供的,面向对象,简单易用,可以直接操作线程对象。不过也需要需要程序员自己管理线程的生命周期(主要是创建),我们在开发的过程中偶尔使用 NSThread。比如我们会经常调用[NSThread currentThread]来显示当前的进程信息。

线程: 指独立执行的代码段
进程:指一个正在运行的可执行程序,它可以包含多个线程

主线程:
1.一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”。
2.处理UI事件,刷新显示UI。

使用注意:
1.不要将耗时操作放到主线程中去处理,会卡住线程。
2.和UI相关的刷新操作必须放到主线程中进行处理。

二、结合案例使用

2.1 NSThread

案例一:子线程下载图片->主线程显示图片

- (IBAction)demo {
//创建线程后自动启动线程
    [NSThread detachNewThreadSelector:@selector(loadImgageDataFormNetwork)  toTarget:self  withObject:nil];
  
}
- (void)loadImgageDataFormNetwork{
    
    NSLog(@"--%@",[NSThread currentThread]);// --<NSThread: 0x604000460040>{number = 3, name = (null)}

    NSURL *url = [NSURL URLWithString:@"https://img.haomeiwen.com/i1658521/929b88123cf7156c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];    
    UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
    
    [self  performSelectorOnMainThread:@selector(updataImg:) withObject:image waitUntilDone:YES];   
}

- (void)updataImg:(UIImage *)image{
    NSLog(@"%@",[NSThread currentThread]); ///<: 0x600000077680>{number = 1, name = main}
    self.imgView.image = image;
}

案例二:售票的小故事(窗口1和窗口2同时售票,售完即止)

引出问题:多个线程访问同一块资源会发生什么??请继续往下看

原汁原味(非线程安全)
- (IBAction)demo1 {
    
    _ticket1 = [[NSThread alloc]initWithTarget:self selector:@selector(buyTicketNoSafe) object:nil];
    _ticket1.name = @"1号售票窗口";
   
    
    _ticket2 = [[NSThread alloc]initWithTarget:self selector:@selector(buyTicketNoSafe) object:nil];
    _ticket2.name = @"2号售票窗口";
    
     [_ticket1 start];
     [_ticket2 start];
    
}
- (void)buyTicketNoSafe{
    
    while (1) {
        
        if (_ticketTotal>0) {
            _ticketTotal --;
            
      NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", _ticketTotal, [NSThread currentThread].name]);

            [NSThread sleepForTimeInterval:0.2];
        }else{
            NSLog(@"不好意思。。票买完了。。。。");
            break;
        }
    }
}
Snip20180205_1.png
改进版(线程安全)

线程安全解决方案:
加锁(在一个线程执行该操作的时候,不允许其他线程进行操作)

- (void)buyTicketSafe{
   while (1) {
        @synchronized(self){
        if (_ticketTotal>0) {
            _ticketTotal --;
            
      NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", _ticketTotal, [NSThread currentThread].name]);

            [NSThread sleepForTimeInterval:0.2];
        }else{
            NSLog(@"不好意思。。票买完了。。。。");
            break;
        }
    }
  }
}

图片.png

*Thread小结:

线程安全解决方案:可以给线程加锁,在一个线程执行该操作的时候,不允许其他线程进行操作,iOS 实现线程加锁有很多种方式。例如:@synchronized、 NSLock、NSRecursiveLock、NSCondition、NSConditionLock、pthread_mutex、dispatch_semaphore、OSSpinLock、atomic(property) set/ge等等各种方式。这里采取synchronized来保证线程安全,从而解决线程同步问题。
@synchronized 也许已经有小伙伴注意到了只是加了一个这个,就解决了售票bug的问题。
@synchronized简称为互斥锁,给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。是为了防止多个线程抢夺同一个资源造成的数据安全问题。
或者使用@autoreleasepool也是一样的。也能解决该问题。
@autoreleasepool是自动释放池,写在@autoreleasepool代码块中,让每次执行完成后可以及时的释放临时对象的内存。

上一篇 下一篇

猜你喜欢

热点阅读