UI基础-多线程

多线程概述

程序 进程 线程

程序

  • 由源代码生成的可执行应用。

进程

  • 一个正在运行的程序可以看做一个进程,进程拥有独立运行所需的全部资源。

线程

  • 程序中独立运行的代码段。
  • 除了主线程以外的 都叫子线程
  • 子线程可以有很多个 但是线程是耗费资源的
  • 一般最多不超过五条 注:3条最佳
  • 程序退出后 会清空线程的任务

三者之间的关系

一个进程是由一个或多个线程组成。进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行。

单、多线程的区别

  • 单线程程序:只有一个线程,代码顺序执行,容易出现代码阻塞(页面假死)。
  • 多线程程序:有多个线程,线程之间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能。
    注意:iOS中关于UI的添加和刷新必须在主线程中操作。

主线程操作什么样的任务?

  • UI界面 按钮点击 屏幕的滚动 (一切用户看得见的 都要在主线程当中去操作)
  • 比较大的耗时操作 又或者 用户看不到的操作 可以放到子线程当中去做 比如:下载 解压缩 读取大型数据等 可以在子线程中去操作

多线程的原理:

  • CPU在工作时 同一时间只能执行一个任务 之所以可以造成多条线程一起执行任务的假象 是CPU在进行高速的切换(调度) 在线程之间切换 来达到多个任务一起执行的效果

说说多线程的优点:

  • 可以大大提高 执行任务的效率
  • 可以让用户 有更好的用户体验

缺点:

  • 如果大量开辟线程 会造成程序的卡顿(耗费过量的资源)

线程休眠

[NSThread sleepForTimeInterval:10];

判断当前线程是否为主线程

 NSLog(@"%d", [NSThread isMainThread]);
- (void)viewDidLoad {
    [super viewDidLoad];

    // 创建Button感受卡顿
    UIButton *button = [UIButton buttonWithType:(UIButtonTypeCustom)];
    button.frame = CGRectMake(100, 100, 100, 100);
    button.backgroundColor = [UIColor orangeColor];
    [button addTarget:self action:@selector(buttonClick:) forControlEvents:(UIControlEventTouchUpInside)];
    [self.view addSubview:button];

    // 查看当前线程的方法
    NSLog(@"%@", [NSThread currentThread]);

}

- (void)buttonClick:(UIButton *)button
{
    NSLog(@"%@", [NSThread currentThread]);
    NSLog(@"你点我了");
}

// 触摸控制器的view 会触发
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//    for (int i = 0; i < 10000; i++) {
//        NSLog(@"%@", [NSThread currentThread]);
//        NSLog(@"---%d", i);
//    }
    // 该方法在哪个线程里调用 该方法的线程就是那个线程
    // [self performSelector:@selector(download:) withObject:@"123"];

    // 开辟了一个子线程
    // 方法是基类提供的 只要是对象都能调用
    // [self performSelectorInBackground:@selector(download:) withObject:@"123"];



#pragma mark ------ NSThread
//    // 初始化 开辟一个子线程
//    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"123"];
//    // 开始执行
//    [thread start];

    // 2.开辟子线程 并且这个方法不用开始 自动执行
    [NSThread detachNewThreadSelector:@selector(download:) toTarget:self withObject:@"123"];

}

- (void)download:(NSString *)str
{
    // 线程休眠 (延迟执行效果)
    [NSThread sleepForTimeInterval:10];

    // 通过在子线程中执行耗时操作 解决卡顿问题
    NSLog(@"download---%@", [NSThread currentThread]);

    // 判断当前线程是否为主线程
    NSLog(@"%d", [NSThread isMainThread]);


    for (int i = 0; i < 10000; i++) {
       // NSLog(@"%@", [NSThread currentThread]);
        NSLog(@"---%d", i);
        if (i == 300) {
           // 立即结束当前线程
            [NSThread exit];
           // 相当于 你在这里
           // 只要退出当前的线程 后面的一切方法都不执行了
            return;
        }
    }
    NSLog(@"你猜还执行么?");
}

NSOperation

  • NSOperation 是一个抽象类 (本身无主线程、子线程之分)实际任务由它的子类
    NSBlockOperation或NSInvocationOperation执行 通常与NSOperationQueue结合使用

NSInvocationOperation

  • 封装了执行操作的target和要执行的action

NSBlockOperation

  • 封装了需要执行的代码块

线程队列(任务中心 可以执行很多任务)

  • 串行队列 主线程相当于一个串行队列 队列中的任务需要一个执行完毕后 再执行下一个
  • 并行队列 队列中的任务 进行并发执行 同时开始未必同时结束

  • 同步:没有开启子线程的能力

  • 异步:拥有开启子线程的能力

任务是什么?

  • 比如 下载 打印 都是个任务

线程之间的通信

在子线程中 完成耗时的操作 完成后 需要回到主线程进行UI刷新

常用的开启多线程的方法

优势:不用程序员 管理 线程的生命周期
- NSOperation 封装一下GCD的方法 提供OC的语法来使用GCD
- GCD 是苹果推荐的 可以最大化地发挥多核CPU 是C语言的函数

NSOperationQueue

  • 是操作队列,用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务的并行执行。
  • NSOperation可以调剂为在队列中的优先级。
  • 当最大并发数设置为1时,能实现线程同步
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // 需要把 NSInvocationOperation 对象 放进一个队列里 才能开启子线程
    // 创建一个队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    // 给队列提供一个  可以设置  最大并发数
    // 一次最多并发执行两个任务
    queue.maxConcurrentOperationCount = 2;

    // 开启一个线程 (相当于一个任务)
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(downLoad:) object:@"123"];
    // 把任务添加到队列中
    // 只需要把任务添加进队列中  不需要程序员操作 会自动执行
    // 不是添加一个任务 就开启一个线程
    // 这个操作是系统给咱们做的 有的线程会被系统重复利用(优化)
    [queue addOperation:operation];
    // 往队列中添加任务
    [queue addOperationWithBlock:^{
        // block 就是 你要添加的任务
        NSLog(@"---3%@", [NSThread currentThread]);
    }];
    // 初始化一个任务
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"----4%@", [NSThread currentThread]);
    }];

    // 添加进队列
    [queue addOperation:blockOperation];
}

- (void)downLoad:(NSString *)str
{
    NSLog(@"%@", [NSThread currentThread]);
}

GCD

任务

  • 具有一定功能的代码段,一般是一个block或者函数
  • 分发队列:GCD以队列的方式进行工作,FIFO。
  • 根据分发队列的类型,创建合适数量的线程执行队列中的任务。

队列类型

  • SerialQueue:一次只执行一个任务,用于同步访问特定的资源或数据。创建多个时,虽然是同步执行的,但两两之间是并发执行的,实现线程同步
  • Concurrent:可以并发第执行多个任务,但是遵守FIFO。

功能

  • dispatch_async() 往对列中添加任务,任务排队执行
  • dispatch_after() 往对列中添加任务,任务不但会排队,还会在延迟的时间点执行
  • dispatch_apply() 往对列中添加任务,任务会重复执行n次
  • dispatch_group_async() 将任务添加到队列中,并添加分组标记
  • dispatch_group_notify() 将任务添加到队列中,当某个分组的所有任务执行完成之后,此任务才会执行
  • dispatch_barrier_async() 将任务添加到队列中,此任务执行的时候,其他任务停止执行
  • dispatch_once() 任务添加到队列中,但任务在程序运行过程中,只执行一次
  • dispatch_sync() 将任务添加到队列中,block不执行完,下面代码不会执行

队列:

  • 并行队列 系统提供了一个 全局并行队列(整个应用都可以使用)如果不想用 也可以创建一个出来
  • 串行队列 需要创建一个出来

任务:

  • 同步:不具有开启线程的能力
  • 异步:具有开启线程的能力

分四种情况:

  • 并行 — 异步任务
  • 并行 — 同步任务
  • 串行 — 异步任务
  • 串行 — 同步任务
- (void)viewDidLoad {
    [super viewDidLoad];
    // [self asyncGlobleQueue];
    // [self syncGlobalQueue];
    // [self asyncSerialQueue];
    // [self sycnSerialQueue];
    // 100张票 4个同时卖票 抢夺资源的问题
    // 多个线程 同时对一个数据进行操作

    // 解决方案:访问数据 加一个锁 只能有一个线程访问 等这个线程访问后 才能让下一个线程再访问 保证数据被访问的安全

    // 添加同步锁 (互斥锁)
    self.lock = [[NSLock alloc] init];

//    [lock lock]; // 上锁
//    // 中间就是被上锁的部分
//    [lock unlock]; // 开锁

//    // 初始化票的总数
//    self.ticketsCount = 100;
//    // 调用一下 买票
//    [self tickets];

#pragma mark --- GCD开启子线程加载图片 回到主线程刷新UI
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        // 做请求 耗时操作(同步请求)
        // 在子线程中的 同步请求 就相当于 异步请求
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:kUrl2]];
        UIImage *image = [UIImage imageWithData:data];
        // 回到主线程 刷新界面
        // 取出主线程 dispatch_get_main_queue();
        dispatch_async(dispatch_get_main_queue(), ^{
            // 回到主线程 要执行的任务
            // 刷新UI
        });
    });
}

// 模拟卖票
- (void)tickets
{
    // 获取并行队列 添加异步任务
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        //  添加异步任务
        [self setTickets];
    });

    dispatch_async(queue, ^{
        //  添加异步任务
        [self setTickets];
    });

}

// 卖票方法
- (void)setTickets
{

    // 互斥锁
    @synchronized(self){
        while (1) {
            if (self.ticketsCount > 0) {
                self.ticketsCount--;
                NSLog(@"剩余%ld张 ------%@", self.ticketsCount, [NSThread currentThread]);
            } else {
                // 票卖没了 停止循环
                return;
            }
        }
    }
}


// 1.并行 --- 异步任务
// 开启子线程 并且 任务并发
- (void)asyncGlobleQueue
{
    // 获取 全局的并发队列
    // dispatch_queue_t  队列的类型
    // 参数1 是cpu切换的优先级(频率高度)
    // 参数2 是个预留参数 可以填0
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 添加异步任务
    // 参数1 给哪一个队列添加任务
    dispatch_async(queue, ^{
        // 添加任务
        NSLog(@"---任务1---%@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        // 添加任务
        NSLog(@"---任务2---%@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        // 添加任务
        NSLog(@"---任务3---%@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        // 添加任务
        NSLog(@"---任务4---%@", [NSThread currentThread]);
    });

    // 创建的队列 是需要释放的(ARC 不用释放)
    dispatch_release(queue);
}

//  2.并行 --- 同步任务
// 没有开启子线程 相当于变成了一个串行的队列
- (void)syncGlobalQueue
{
    // 创建一个 并行队列
    // 参数1 队列的标识符 反向域名  com.lanou3g.www
    // 参数2 填 你要创建的队列的类型(串/ 并)
    dispatch_queue_t queue = dispatch_queue_create("com.lanou3g.www", DISPATCH_QUEUE_CONCURRENT);

    // 添加同步任务
    dispatch_sync(queue, ^{
        // 添加同步任务
        NSLog(@"---任务1---%@", [NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        // 添加任务
        NSLog(@"---任务2---%@", [NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        // 添加任务
        NSLog(@"---任务3---%@", [NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        // 添加任务
        NSLog(@"---任务4---%@", [NSThread currentThread]);
    });

}

// 3.串行 --- 异步任务
// 开启了子线程 串行
- (void)asyncSerialQueue
{
  // 创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("哈哈", DISPATCH_QUEUE_SERIAL);
    // 添加异步任务
    dispatch_async(queue, ^{
        // 添加任务
        NSLog(@"---任务1---%@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        // 添加任务
        NSLog(@"---任务2---%@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        // 添加任务
        NSLog(@"---任务3---%@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        // 添加任务
        NSLog(@"---任务4---%@", [NSThread currentThread]);
    });
    dispatch_release(queue);
}

//  4.串行 --- 同步任务
// 没有开启线程 都在主线程 顺序执行
- (void)sycnSerialQueue
{
    dispatch_queue_t queue = dispatch_queue_create("haha", DISPATCH_QUEUE_SERIAL);
    // 添加同步任务
    dispatch_sync(queue, ^{
        // 添加任务
        NSLog(@"---任务1---%@", [NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        // 添加任务
        NSLog(@"---任务2---%@", [NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        // 添加任务
        NSLog(@"---任务3--%@", [NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        // 添加任务
        NSLog(@"---任务4---%@", [NSThread currentThread]);
    });

    dispatch_release(queue);
}

结果

  • 并行 — 异步任务
    这里写图片描述
  • 并行 — 同步任务
    这里写图片描述
  • 串行 — 异步任务
    这里写图片描述
  • 串行 — 同步任务
    这里写图片描述

多线程管理

线程间通信

  • 主线程进入子线程
  • 子线程回到主线程
    这里写图片描述

线程互斥

  • 线程互斥是指某一资源同时只允许一个访问者对齐进行访问,具有唯一性和排他性。
  • 访问是无序的。因此要加上互斥锁来进行顺序访问。
  • NSLock类可以协助完成互斥操作。

单例类的创建

+ (MyHandle *)shareHandle
{
    // 如果同时有多个线程 访问这个对象
    // 如果创建该对象
  static MyHandle *handle = nil;
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
  // 执行的任务 在整个程序运行期间 只执行一次
  // 并且只允许 一个线程访问(自带 线程保护)
  handle = [[MyHandle alloc] init];
  });
  return handle;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值