YOU'VE MADE A BRAVE DECISION, WELCOME.

每一个不曾起舞的日子都是对生命的辜负。

多线程及其相关的概念

多线程及其相关的概念

  • 进程

    • 进程是指在系统正在运行的一个应用程序

    • 比如同时打开微信、QQ,系统就会分别启动两个进程

    • 每个进程之间是独立的,且运行在其专用并受保护的内存空间内
  • 线程

    • 线程是进程的基本执行单元,一个进程想要执行任务,必须的有线程(一个进程至少有一个线程)
    • 比如qq进行文字聊天,使用微信进行视频聊天,都需要在线程中执行
  • 线程的串行

    • 如果一个线程中执行多个任务,那么只能一个一个的按顺序执行,也就是说,在同一时间内,一个线程中只能执行一个任务
    • 比如在一个线程中3个下载任务(任务A,任务B,任务C)
    • 因此也认为线程是进程中的一条执行路径
  • 多线程

    • 一个进程中可以开启多条线程,每个线程可以并行(同时)执行不同的任务
      • 可以将进程比作工厂车间,线程比作车间工人,由不同任务的车间工人在车间运作,最后生产出产品。因此呢,可以说多线程技术可以提高程序的执行效率
    • 比如同时开启3条线程分别操作3个下载任务(任务A,任务B,任务C)
  • 多线程的原理

    • 同一时间,CPU只能处理一条线程,只有一条线程在工作
    • 多线程并发执行时,其实是CPU快速的在线程之间调度
    • 如果CPU调度线程的时间足够快,就造成了多条线程并发执行的假象
  • 多线程的优缺点

    • 优点
      • 能够适当的提高程序的执行效率
      • 能够适当提高资源的利用率(CPU、利用率)
    • 缺点
      • 开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
      • 线程越多,CPU在调度线程上得开销就越大
      • 程序设计更加复杂:比如线程之间通信,多线程的数据共享
  • 主线程

    • 一个iOS程序运行后,默认会开启一条线程,该线程称为“主线程”或“UI线程”
    • 显示/刷新UI界面
    • 处理UI事件(比如点击事件,滚动事件,拖拽事件)
    • 不要将比较耗时的操作放在主线程中(因为耗时操作会卡主主线程,严重影响UI的流畅度,会给用户一种“卡”的感觉,体验效果不好)
  • 多线程的使用方案

    • | 技术方案 | 简介 | 语言 | 线程的生命周期 | 使用频率 |
      | :———: | —————————————- | :–: | :—–: | :–: |
      | NSThread | 1.面向对象的使用 2.简单易用可以操作线程对象 | OC | 程序员管理 | 偶尔使用 |
      | GCD | 1.旨在代替NSThread等线程技术 2.充分利用设备的多核 | C | 自动管理 | 经常使用 |
      | NSOperation | 1.基于GCD 2.使用更加面向对象 3.比GCD多了一些更简单的使用的功能 | OC | 自动管理 | 经常使用 |
  • NSThread创建多线程方法

    • 1
      2
      NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"abc"];
      [thread start];
    • [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"miss"];

    • [self performSelectorInBackground:@selector(run:) withObject:@"back"];

  • 互斥锁

    • 优点
      • 有效的防止因多线程抢夺资源造成的数据安全问题
    • 缺点
      • 因为线程等待,需要消耗大量的CPU资源
    • 注意
      • 互斥锁又叫线程同步
  • 线程同步

    • 线程同步指的是多条线程在同一条线上执行(按顺序执行任务)

    • 1
      @synchronized (self){}
  • 线程通信

    • 在一个进程中,线程往往不是孤立存在的,多个线程之间是存在有通信关系的
  • 线程通信的体现

    • 一个线程传递数据给另外一个线程
    • 一个线程执行完成任务后转到另外一个线程继续执行任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//在主线程使用这种耗时的方法会消耗主线程,所以需要使用到子线程来回调
NSDate *begin = [NSDate date];
NSURL *url = [NSURL URLWithString:IMAGE_URL];
NSDate *end = [NSDate date];
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(@"%f",[end timeIntervalSinceDate:begin]);
self.imageView.image = [UIImage imageWithData:data];
//线程通信----由子线程回归到主线程
NSURL *url = [NSURL URLWithString:IMAGE_URL];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
//方法一
[self performSelectorOnMainThread:@selector(loadImage:) withObject:image waitUntilDone:YES];
//方法二
[self performSelector:@selector(loadImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
//方法三
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
//然后通过调用的方法传值
- (void) loadImage:(id)obj {
self.imageView.image = obj;
}
  • GCD
    • 纯C语言,提供了非常强大的函数
    • 优势
      • GCD是苹果公司为多核的并行运算提出的解决方案
      • GCD会自动利用更多的CPU内核(比如双核,四核)
      • GCD会自动管理线程的生命周期(创建线程,调度任务,销毁线程)
      • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
    • 任务:执行什么操作
      • 同步任务
        • Dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);
        • queue:队列
        • block:任务
      • 异步任务
        • Dispatch_async(dispatch_queue_t queue,dispatch_block_t block);
      • 同步异步:能不能开启新的线程
        • 同步:
          • 只能在当前线程中执行任务,不具备开启新线程的能力
        • 异步:
          • 可以再新的线程中执行任务,具备开启新线程的能力
    • 队列:用来存放任务
      • 并发与串行:任务的执行方式
        • 并发队列(ConCurrent Dispatch Queue)
          • 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
          • 并发的功能只有在异步函数(dispatch_async)下才有效
          • dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)创建并行队列
            • label:表示队列的名字(标识)
            • attr:表示队列的属性,根据这个设置判断是并行队列还是串行队列(DISPATCH_QUEUE_CONCURRENT表示并行队列)
          • Dispatch_get_global_queue(dispatch_queue_priority_t priority, unsigned long flags);获得全局的并发队列
            • Priority:队列的优先级(分为高,默认,低,后台)
            • flags:此函数暂时无用,用0即可
        • 串行队列(Serial Dispatch Queue)
          • 让任务一个接着一个的执行(也就是说须等一个任务执行完毕后才可执行下一个任务)
          • dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)创建串行队列
            • label:表示队列的名字(标识)
            • attr:表示队列的属性,根据这个设置判断是并行队列还是串行队列(DISPATCH_QUEUE_SERIAL或者NULL表示串行队列)
          • 使用主队列(与主线程相关联的队列)
            • 使用dispatch_get_main_queue()获得主队列
    • 步骤:
      • 定制任务—-确定想做的事情
      • 将任务添加到队列中—-GCD会自动将队列的任务取出,放到对应的线程中执行
    • 注意:
      • 任务的取出遵循队列的FIFO原则:先进先出,后进后出