iOS多线程-NSThread简单介绍

线程是进程中的一条执行路径,在一个线程中任务的执行是串行的,同一时间内,一个线程只能执行一个任务

多线程
  • 在多线程开发中, 耗时操作我们一般放在子线程。耗时操作会卡住主线程,严重影响UI流畅度。
  • 一个进程中可以开启多条线程,多条线程可以并发(同时)执行不同的任务。
多线程特点
  • 同一时间,CPU(单核)只能处理1条线程,只有一条线程在工作(执行)。
  • 多线程并发(同时)执行,其实是CPU(单核)快速在多条线程之间调度(切换)。
  • 多核CPU是真正意义上的多线程。
  • 如果线程太多, CPU在多个线程之间调度,会消耗大量的CPU资源,每条线程被调度执行的频次会降低(线程执行效率会降低)。
多线程的优点:

能适当提高程序的执行效率。

缺点:
  • 创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB、主线程1MB),大约需要90毫秒的创建时间。
  • 开启大量的线程,会降低程序的性能,CPU在调度线程上的开销很大。
  • 程序设计更加复杂:比如线程之间的通信、多线程的数据共享。
主线程
  • 一个iOS程序运行后,默认开启一个线程,称为主线程或UI线程。
  • 显示、刷新UI界面。
  • 处理UI事件(点击、滚动、拖拽事件等)。

1, NSThread 方法简单介绍

初始化NSThread对象
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
或者
NSThread *thread = [[NSThread alloc] initWithBlock:^{
        
}];

执行任务
[thread start];
把任务执行在分离新线程中
[NSThread detachNewThreadSelector:@selector(demo2:) toTarget:self withObject:nil];
或者
[NSThread detachNewThreadWithBlock:^{
        
}];
在后台线程执行任务
[self performSelectorInBackground:@selector(demo3:) withObject:@"backgrond"];

回到主线程执行任务
[self performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>]
线程的状态
  1. 创建线程之后,系统会把线程放在可调度线程池中,当调用 start 之后,线程就变成 Runnable (就绪)状态。
  2. 之后 CPU 调度当前线程运行, 之后再切换到其他线程。
  3. 通多sleep 方法或者等待同步锁,可以使线程处于阻塞状态, 阻塞的线程会移出可调度线程池,停止参与调度。
  4. 等 sleep 时间到或者等待同步锁打开,会被重新放在x可调度线程池中,继续参与调度(执行任务)。
  5. 当线程执行完毕或者强制退出,线程处于死亡状态。
// 1 创建线程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(sleepStatus) object:nil];
    // 2,线程就绪状态
    [thread start];

- (void)sleepStatus {
    // 3, 阻塞-sleep,当运行满足某个条件,会让线程睡
    // 提示: sleep 是类方法,会直接休眠当前线程
    NSLog(@"睡一会");
    [NSThread sleepForTimeInterval:2];
    
    for (int i = 0; i < 20; i++) {
        NSLog(@"%d, %@", i, [NSThread currentThread]);
        // 4, exit -- 强行终止: 类方法,强制停止当前线程, 一旦强行终止,后续的代码都不会执行
        // exit -- 会杀掉主线程,但是 APP 不会崩溃
        if (i == 12) {
            [NSThread exit];
        }
    }
}
线程安全问题

问题: 当剩余 20 张火车票,两台机器同时售卖,A 机器读取剩余 20, 卖一张还剩 19, B机器读取剩余 20,卖一张还剩 19(相当于两条线程同时访问同一块内存)

测试代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    self.tickets = 20;
    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(threadDemo) object:nil];
    thread1.name = @"线程 A";
    [thread1 start];
    
    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(threadDemo) object:nil];
    thread2.name = @"线程 B";
    [thread2 start];
}

- (void)threadDemo {
    while (YES) {
        [NSThread sleepForTimeInterval:1.0];
            if (self.tickets > 0) {
                self.tickets--;
                NSLog(@"剩下: %ld 张票\n%@", self.tickets, [NSThread currentThread]);
            } else {
                NSLog(@"票已经卖完了!");
                break;
            }
    }
}

打印结果:
2019-07-02 15:52:18.353072+0800 005--NSThread资源共享[1766:741375] 剩下: 19 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:18.353080+0800 005--NSThread资源共享[1766:741376] 剩下: 18 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:19.358301+0800 005--NSThread资源共享[1766:741375] 剩下: 17 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:19.358352+0800 005--NSThread资源共享[1766:741376] 剩下: 16 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:20.360759+0800 005--NSThread资源共享[1766:741376] 剩下: 15 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:20.363214+0800 005--NSThread资源共享[1766:741375] 剩下: 14 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:21.361613+0800 005--NSThread资源共享[1766:741376] 剩下: 13 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:21.367609+0800 005--NSThread资源共享[1766:741375] 剩下: 12 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:22.367059+0800 005--NSThread资源共享[1766:741376] 剩下: 11 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:22.372847+0800 005--NSThread资源共享[1766:741375] 剩下: 10 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:23.372406+0800 005--NSThread资源共享[1766:741376] 剩下: 9 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:23.378088+0800 005--NSThread资源共享[1766:741375] 剩下: 8 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:24.377758+0800 005--NSThread资源共享[1766:741376] 剩下: 7 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:24.380684+0800 005--NSThread资源共享[1766:741375] 剩下: 6 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:25.383125+0800 005--NSThread资源共享[1766:741376] 剩下: 5 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:25.384562+0800 005--NSThread资源共享[1766:741375] 剩下: 4 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:26.388494+0800 005--NSThread资源共享[1766:741376] 剩下: 3 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:26.388533+0800 005--NSThread资源共享[1766:741375] 剩下: 2 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:27.393722+0800 005--NSThread资源共享[1766:741376] 剩下: 1 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:27.393722+0800 005--NSThread资源共享[1766:741375] 剩下: 1 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:28.398984+0800 005--NSThread资源共享[1766:741375] 剩下: 0 张票
<NSThread: 0x282b31200>{number = 4, name = 线程 A}
2019-07-02 15:52:28.398984+0800 005--NSThread资源共享[1766:741376] 剩下: 0 张票
<NSThread: 0x282b31240>{number = 5, name = 线程 B}
2019-07-02 15:52:29.400585+0800 005--NSThread资源共享[1766:741375] 票已经卖完了!
2019-07-02 15:52:29.404393+0800 005--NSThread资源共享[1766:741376] 票已经卖完了!

打印结果的最后几行出现剩余相同张数的情况(每次执行结果都可能不一样,在多线程的开发中不要相信一次的运行结果)。

通过添加互斥锁解决问题:

把改成
// 互斥锁 -- 保证锁内的代码,同一时间只有一条线程执行
// 互斥锁的范围,应该尽量小,范围大了,效率就低
@synchronized (self) {
      if (self.tickets > 0) {
         self.tickets--;
         NSLog(@"剩下: %ld 张票\n%@", self.tickets, [NSThread currentThread]);
      } else {
          NSLog(@"票已经卖完了!");
          break;
      }
}

多线程卖票的问题就解决了。自己动手试试吧!!!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,063评论 6 510
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,805评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,403评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,110评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,130评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,877评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,533评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,429评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,947评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,078评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,204评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,894评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,546评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,086评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,195评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,519评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,198评论 2 357

推荐阅读更多精彩内容