二. NSThread基本使用

一. 线程的创建

  1. 创建线程并且手动开启, 同时在这条线程执行selector的任务

     // 1. 创建线程对象
     NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(task1:) object:@"创建线程方式1"];
     // 2. 为线程添加标识   
     [thread1 setName:@"thread1"];           
     // 3. 设置线程的优先级(优先级高的线程,会优先执行)       
     [thread1 setThreadPriority:0.5];       
     // 4. 开始执行
     [thread1 start];
    
  2. 分离出一条子线程, 同时在这条线程执行selector的任务

     // 2. 分离子线程
     [NSThread detachNewThreadSelector:@selector(task1:) toTarget:self withObject:@"创建线程方式2"];           
    
  3. 开启一条后台线程, 同时在这条线程执行selector的任务(注意, 该方法是由当前控制器调用的)

       // 3. 创建后台线程
       [self performSelectorInBackground:@selector(task1:) withObject:@"创建线程方式3"];
    
  4. 自定义线程

    • 创建一个NSThread的子类
    • 在类中重写-(void)main方法, 将这个线程需要执行的任务, 在这个方法中实现
    • 使用alloc/init创建这个自定义线程, 当使用start执行这个线程的时候, 系统会自动调用main方法来执行其中的任务
  5. 创建线程方法的优缺点

    • 手动创建线程, 可以获取线程对象, 可以对这个线程对象进行详细的设置, 如标识/优先级等
    • 而采用分离子线程或者开启后台线程的方法, 使用方法简单, 可以直接开启一个子线程去执行耗时任务, 但是由于无法获取到这个线程对象, 因此无法对其进行设置

二. 线程的生命周期

  • 线程的五种状态
    • New: 一个刚刚新建出来的线程, 此时没有任何任务在执行当中
    • Runable: 处于可调度线程池的线程, 只有在线程池中的线程, 才可以被CPU调度
    • Running: 使用了-(void)start方法, 被激活工作, 且正在被CPU调度的线程, 一个线程如果还未完成它所有的任务, 那么他就会一直在Runable和Running两个状态中相互切换
    • Blocked: 调用了[NSThread sleepForTimeInterval]方法或等待同步锁的放行时的状态, 此时线程暂时被移除可调度线程池, 但是并没有被销毁掉, 当sleep的时间到了或同步锁放行, 就会恢复Runable状态
    • Dead: 当一条线程的任务执行完毕/异常或强制退出exit方法时, 这条线程就会被销毁, 销毁后的线程与对象一样, 不能再次使用

三. 线程安全

  1. 多线程应用时的安全隐患
    • 当多个线程, 出现访问同一块资源的时候, 这样会导致在存取的过程中, 被取资源中保存的值由于可能同时在修改, 导致值发生错误

      // 例子: 三个售票员同时售票
        - (void)sale {
           while (1) {
               @synchronized(self) {  // 如果这里不增加线程锁, 就会导致存取错误
                   // 1. 检查余票
                   int count = self.count;
                   if (count > 0) {
                       // 演示耗时操作
                       for (int i = 0; i < 100000; i++) {
                           // 如果线程的任务中有耗时操作,就有可能引起共同访问一块资源导致数据错误
                           // 因此这时需要加入线程锁
                       }
                       // 访问属性
                       self.count = count - 1;
                       NSLog(@"%@卖出去了一张票,还剩%d张", [NSThread currentThread], self.count);
                       
                   } else {
                       NSLog(@"票已经卖完");
                       [NSThread exit];
                   }
               }
           }
        }
      
    • 解决方法: 在线程要访问资源之前, 增加一个互斥锁
      @synchronized(锁对象){ 需要锁定的代码 }

    • 互斥锁:

      • 通常锁对象, 是全局唯一的一个对象, 一般使用NSObject作为对象的类型
      • 注意点
        1. 一份代码, 只能使用同一把互斥锁
        2. 锁对象本身有两种状态: 打开/关闭
        3. 当互斥锁关闭的时候, 队列中的线程就会进入Blocked状态, 直到互斥锁打开, 该线程才会进入运行状态
        4. 锁对象可以使用当前的控制器, 也就是self
        5. 加锁的位置需要注意, 位置不同, 执行的代码也会不同
        6. 加锁的前提条件: 只有出现多个线程同时访问同一块资源的时候, 才需要使用互斥锁进行保护
        7. 互斥锁的使用, 会增加额外的资源消耗, 所以能不使用就不要使用, 尽量避免出现多线程抢夺资源
        8. 互斥锁会造成线程同步: 每个线程会在锁的外面排队执行任务
        9. 但是队列中的线程是异步的: 到底是哪条线程访问锁内的资源, 顺序是不确定的

四. 原子和非原子性

  1. atomic:

    • 属性为原子性, 该关键字会为setter方法增加一个互斥锁, 因此它是线程安全的
    • 但是他会消耗大量的内存资源, 并且在我们的开发过程中, 很少发生多线程访问同一个属性的情况, 因此基本不会使用这个关键字
  2. nonatomic:

    • 属性为非原子性, 此关键字不会为setter方法增加互斥锁, 因此是非线程安全的, 适合内存小的移动设备, 即iOS开发中属性主要使用的关键字,
    • 因此, 在日常开发中, 尽量要避免多线程抢夺资源的情况, 加锁/资源抢夺的业务逻辑通常会交由服务器端来处理, 即每次只能接收/发送一份网络请求

五. 线程间的通信

  1. 需求:
    • 开启条子线程, 并且下载一张图片

    • 将获得到的图片, 在主线程中设置给imageView

    • 注意点: 遵循耗时操作交给子线程, UI操作回到主线程

        // 0. 创建子线程执行下载任务线程
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
        [thread start];
        
        - (void)download {
        // 计算时间(绝对时间)
        CFTimeInterval start = CFAbsoluteTimeGetCurrent();    
        // 1. 创建url路径
        NSURL *url = [NSURL URLWithString:@"http://dimg07.c-ctrip.com/images/tg/946/212/497/81b56770ed4544a6a8a1125fb381753d_C_640_640.jpg"];
        // 2. 将图片转换为二进制数据
        NSData *data = [NSData dataWithContentsOfURL:url];    
        // 3. 转换格式(二进制 -> UIImage)
        UIImage *image = [UIImage imageWithData:data];    
        // 4. 回主线程设置图片
        //    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO]; // 该方法可以直接使用主线程去执行任务
        [self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];    
        // 获取绝对时间
        CFTimeInterval end = CFAbsoluteTimeGetCurrent();    
        NSLog(@"%f", end - start);
        }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 一、多线程基础 基本概念 进程进程是指在系统中正在运行的一个应用程序每个进程之间是独立的,每个进程均运行在其专用且...
    AlanGe阅读 546评论 0 0
  • 目录: (一)线程与进程之间的区别 (二)为什么需要学习多线程 (三)多线程任务执行方式 (四)多线程执行的...
    KingLionsFrank阅读 776评论 6 6
  • 多线程基本概念 单核CPU,同一时间cpu只能处理1个线程,只有1个线程在执行 。多线程同时执行:是CPU快速的在...
    WeiHing阅读 702评论 1 5
  • 进程与线程 进程:计算机操作系统分配资源的单位,是指系统中正在运行的应用程序,进程之间相互独立,运行在受保护的内存...
    三十六_阅读 375评论 1 1
  • 凡是跟UI相关的都是在主线程执行的 UIKit类库的线程都是不安全的 所以我们需要在主线程上更新UI 因此主线程又...
    xwf_code阅读 593评论 0 0