美文网首页
多线程-NSThread

多线程-NSThread

作者: 蚂蚁牙齿不黑 | 来源:发表于2016-04-06 10:32 被阅读70次

NSThread

每个iOS应用程序都有个专门用来更新显示UI界面、处理用户触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞

创建一条线程
方式一 动态方法
    // 1.创建子线程
    /*
     Target: 子线程需要调用谁的方法
     selector: 被子线程调用的方法
     object: 调用方法时, 给方法传递的参数
     */
    // 注意: 如果线程正在执行, 那么系统会自动强引用NSThread  当线程中的任务执行完毕, 系统会自动释放线程, 对线程进行一次release操作
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo:) object:@"lnj"];
  
    //  给线程取名字
    thread.name = @"子线程1";
    // 设置线程的优先级, 取值范围0.0~1.0, 1.0是最高, 默认就是0.5
    // 注意点: 线程的优先级越高, 那么被CPU调度的可能性就越大  但是并不代表着, 优先级高的一定会先执行
   
    thread.threadPriority = 0.0;
    // 2.启动线程    如果通过alloc/init创建NSThread, 那么需要手动启动线程
    [thread start];
方式二 静态方法
/*
     优点:
     - 使用简便
     - 如果通过detach方法创建线程, 不需要手动调用start启动线程 \ 系统会自动启动
     缺点:
     - 不可以进行其它设置
     应用场景:
     - 如果仅仅需要简单的开启一个子线程执行一些操作, 不需要对子线程进行其它设置, 那么推荐使用detach方法创建线程
     */
    [NSThread detachNewThreadSelector:@selector(demo:) toTarget:self withObject:@"canshu"];
方式三 隐式创建线程
  // 系统会自动在后台给我们开启一个子线程, 并且会自动启动该线程执行任务
    [self performSelectorInBackground:@selector(demo:) withObject:@"oooo"];

线程通信

场景:从网络下载一张图片,然后返回主线程更新UI

开启一个子线程下载图片

[self performSelectorInBackground:@selector(downlod) withObject:nil];

实现下载图片方法

- (void)downlod
{
    NSLog(@"%@", [NSThread currentThread]);  //   打印当前线程
    // 1.下载图片
     NSURL *url = [NSURL URLWithString:@"http://pic.4j4j.cn/upload/pic/20130531/07ed5ea485.jpg"];
     NSData *data = [NSData dataWithContentsOfURL:url];
    // 2.将二进制转换为图片
    UIImage *image = [UIImage imageWithData:data];  
}

拿到图片后 返回主线程更新UI 方式一

     /*
     waitUntilDone: 
     YES: 如果传入YES, 那么会等待updateImage方法执行完毕, 才会继续执行后面的代码
     NO:  如果传入NO, 那么不会等待updateImage方法执行完毕, 就可以继续之后后面的代码
     */
    // 可以在指定的线程中, 调用指定对象的指定方法
    [self performSelector:@selector(updateImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];

拿到图片后 返回主线程更新UI 方式二 工作中常用

//  这个方法也就不需要再重新实现更新UI的方法了
 [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];

实现更新UI的方法

- (void)updateImage:(UIImage *)image
{
    NSLog(@"%@", [NSThread currentThread]);
    self.imageView.image = image;
}

在当前线程执行操作

[self performSelector:@selector(run) withObject:nil]; 

多条线程在同一时间访问同一块资源容易出现安全隐患

模拟场景:12306服务器设置了20张从北京到上海火车票,有三个售票员用他们的电脑进行售票,这里要需要用到互斥锁 它的作用就是锁住这块资源让它在同一时间只能让一条线程访问 接下来先测试加锁与不加锁的区别

创建3条线程代表3个售货员
- (void)viewDidLoad {
    [super viewDidLoad];
    // 0 初始化票数
    self.totalCount = 100;
    
    // 1 创建3个售票员
    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    thread1.name = @"售票员1";
    self.thread1 = thread1;
    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    thread2.name = @"售票员2";
    self.thread2 = thread2;
    NSThread *thread3 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    thread3.name = @"售票员3";
    self.thread3 = thread3;
}
开始售票
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 2.让3个售票员同事售票
    [self.thread1 start];
    [self.thread2 start];
    [self.thread3 start];
}
售票进行中 不加锁
- (void)saleTicket
{
    while (1) {
        
        NSLog(@"欢迎光临");
            // 1.查询剩余的票数
            NSUInteger count = self.totalCount;
            // 2.判断是否还有余票
            if (count > 0) {
                // 线程1   让线程处于休眠状态   模拟售票时间为0.1秒
                [NSThread sleepForTimeInterval:0.1];
                // 2.1卖票
                self.totalCount = count - 1; // 99
                NSLog(@"%@卖了一张票, 还剩%zd票", [NSThread currentThread].name, self.totalCount);
            }else
            {
                // 3.提示客户, 没有票了
                NSLog(@"对不起, 没有票了");
                break;
            }
    }
    
}
看打印结果

明显线程没有获取到最新的值 造成数据访问错误


Paste_Image.png
售票进行中 加锁
// 售票方法
- (void)saleTicket
{
    while (1) {
        
        NSLog(@"欢迎光临");
        @synchronized (self) { //@synchronized  为互斥锁    self可以为任何对象  但必须是同一对象  保证单例
            
            // 1.查询剩余的票数
            NSUInteger count = self.totalCount;
            // 2.判断是否还有余票
            if (count > 0) {
                // 线程1   让线程处于休眠状态   模拟售票时间为0.1秒
                [NSThread sleepForTimeInterval:0.1];
                // 2.1卖票
                self.totalCount = count - 1; // 99
                NSLog(@"%@卖了一张票, 还剩%zd票", [NSThread currentThread].name, self.totalCount);
            }else
            {
                // 3.提示客户, 没有票了
                NSLog(@"对不起, 没有票了");
                break;
            }
        }

    }
    
}
这下就没有问题了
Paste_Image.png

相关文章

网友评论

      本文标题:多线程-NSThread

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