多线程(一)

作者: Shawn_ | 来源:发表于2016-05-21 08:55 被阅读80次

基本概念

  1. 进程:系统中正在运行的一个应用程序。
  2. 线程:进程想要执行任务,必须要有线程(每1个进程至少要有个1条线程),线程是进程的基本单元,一个进程的所有任务都是在线程中执行。
  3. 线程的串行:1个线程中任务的执行时串行的,若1个线程中执行多个任务,只能一个个的按顺序执行。同一时间内,1个线程只能执行1个任务。
  4. 多线程
    • 一个进程中开启多条线程,每条线程可以并行(同时)执行不同的任务
    • 什么叫线程的并行
      • 并行即同时执行。若同时开启3条线程分别下载3个文件(文件A,文件B,文件C)
    • 多线程并发执行的原理
      • 同一时间CPU只能处理1条线程。多线程并发执行,只是CPU在多条线程之间进行切换,如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象。

5.多线程的优缺点

  • 优点
    • 能适当提高程序的执行效率
    • 能适当提高资源利用率(CPU,内存利用率)
  • 缺点
    • 开启线程需要占用一定的内存空间,若开启大量的线程,则会占用大量的内存空间,降低程序的性能
    • 线程越多,CPU在调度线程上的开销就越大
    • 程序设计更加复杂(线程之间的通信,多线程的数据共享)

6.iOS中多线程的实现方案

- pthread
- NSThread
- GCD
- NSOperation

NSThread

  • 创建方式

    • [alloc init];
      • 需要手动开启线程;
      • 可以拿到线程对象
  • 分离一条子线程;
    - 不可拿到线程对象;

  • 后台创建一条线程;

    • 不可拿到线程对象;
  • 线程状态

  • 新建

  • 就绪

  • 运行

  • 阻塞

  • 死亡(线程死亡之后,不能再重新启用)

  • 线程安全

    • 多个线程访问同一块区域
    • 增加互斥锁
    • 相关代码:@synchoronized(self){}(一般都是使用self)
    • 这叫线程同步
  • 原子属性和非原子属性

  • 用@porperty生命一个属性的时候,内部会进行三步操作
    1.生成一个有下划线的成员变量
    2.生成一个setter方法
    3.生成一个getter方法

  • 原子属性 : atomic(为setter方法加锁,默认就是atomic)

  • 非原子属性:nonatomic(不会为setter方法加锁)

  • 为什么开发中经常使用nonatomic而不是atomic

    • atomic 代表线程安全,需要消耗大量资源
    • nonatomic代表非线程安全,适合内存小的移动设备
    • 在开发中尽量使用nonatomic
    • 尽量避免多线程访问同一资源
    • 尽量将加锁、资源抢夺的业务逻辑交给服务器,从而减少客户端的压力
  • 线程间通信

  • 把程序中的耗时操作尽量放到子线程当中进行

  • 必须要回到主线程中进行UI刷新

GCD

  • 两个核心概念

    • 任务 : 执行什么操作
    • 队列 : 用来储存任务的
  • 同步函数/异步函数

  • 同步函数:只能在当前线程中进行任务,不具备开启子线程的能力,立刻马上执行任务

  • 异步函数:可以在新线程中执行任务,具备开启子线程的能力

  • 并发队列/串行队列

  • 并发队列 : 多个任务可以同时执行,前提是在异步函数的情况下

  • 可以创建并发队列,也可以获取并发队列。GCD里本身存在一个并发队列

  • 串行队列 : 任务不能同时执行,需一个接一个的执行

  • 主队列 : 放在主队列里面的任务只能在主线程中执行,并且也是一个接一个的执行任务

  • GCD的使用

  • 异步函数 + 并发队列 : 开启子线程,并发执行任务

     //1.创建队列
    dispatch_queue_t  queue =          dispatch_queue_creat("one",DISPATHC_QUEUE_CONCURRENT);
     //2.封装任务,将任务添加到队列中
    dispathc_async(queue,^{
    
    NSLog(@"xxxx");
    
    });
    
  • 异步函数 + 串行队列 : 开启一条线程,串行执行任务

 //1.创建队列
 dispatch_queue_t queue = dispatch_queue_creat("two",DISPATCH_QUEUE_SERIAL);

 dispatch_async(queue,^{

  NSLog(@"xxxx");

  });
  • 同步函数 + 并发队列 : 不开线程,串行执行任务
  //1.创建队列
  dispatch_queue_t queue = dispatch_queue_creat("three",DISPATCH_QUEUE_CONCURRENT);

  dispatch_sync(queue,^{

  NSLog(@"xxx");

  });
  • 同步函数 + 串行队列 : 不开线程,串行执行任务
//1.创建队列
   dispatch_queue_t queue = dispatch_queue_creat("four",DISPATCH_QUEUE_SERIAL);
  dispatch_sync(queue,^{

    NSLog(@"xxx");
  });
  • 异步函数 + 主队列 : 不开线程,在主线程中串行执行任务
   //1.获得主队列
  dispatch_queue_t queue = dispatch_get_main_queue();
  //
  dispatch_async(queue,^{

     NSLog(@"xxx");

  });

  • 同步函数 + 主队列 : 造成死锁状况

    //获得主队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    

//
dispatch_sync("six",^{

NSLog(@"xxx");
});


 
- GCD中的线程通信

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    
    NSURL * url = [NSURL URLWithString:@"http://static.jstv.com/img/2016/5/18/20165181463528366178_0.jpg"];
    
    NSData * imagedate = [NSData dataWithContentsOfURL:url];
    
    UIImage * image = [UIImage imageWithData:imagedate];
    
    
    dispatch_async(dispatch_get_main_queue(), ^{
        
        self.imageView.image = image;
    });
  
    
});

}

 
- GCD的常用函数
 - 延迟

![GCD中的延迟](http:https://img.haomeiwen.com/i1404354/5a589a7e984c4150.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![调用延迟方法](http:https://img.haomeiwen.com/i1404354/b691033dde114ed0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

![NSTimer](http:https://img.haomeiwen.com/i1404354/63a320ee97f5eea8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
 - 一次代码       

![一次代码](http:https://img.haomeiwen.com/i1404354/18b974230d99a0ab.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 栅栏函数
    
    - 用于异步函数
    - 用于控制多线程执行任务顺序
    - 在使用栅栏函数的时候,*不能使用全局并发队列,只能进行手动创建*。
    - 栅栏函数之前的线程执行顺序,栅栏函数是没有办法进行控制的
![栅栏函数](http:https://img.haomeiwen.com/i1404354/7b9aa4054951553b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 快速迭代

 - 开启多个线程,完成快速迭代操作
 - 类似于for循环
 - GCD里面的快速迭代是并发队列
 - for循环里面是串行队列
        小案例:图片的移动
        思路:(使用了GCD里面的快速迭代)
         1.获得最初文件夹的路径
         2.获得目的文件夹的为路径
         3.移动文件需要全路径,需要对最初文件夹下的文件进行路径拼接
         4.文件名不变,所以目的文件夹的文件路径也需要进行拼接
         5.然后用文件管理者进行文件移动

- 案例代码





dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {

  1. 第一个参数:遍历的次数
  2. 第二个参数: 队列,必须使用并发队列
    3.第三个参数:设置索引

};


![GCD快速迭代 图片移动](http:https://img.haomeiwen.com/i1404354/b1633fd217ea64d3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
- 队列组

  - 用来控制队列任务的完成情况
  
// 创建队列组
dispatch_group_t group = dispatch_group_create();

// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

//将队列添加到队列组中,执行任务(下载图1)
dispatch_group_async(group, queue, ^{
    
    //确定图片地址
    NSURL * url = [NSURL URLWithString:@"http://img.2258.com/d/file/yule/mingxing/neidi/2016-04-20/6b6d95c044b5282cf5b8c78f73c23c4c.jpg"];
    
    //根据图片地址下载二进制数据
    NSData * imageData = [NSData dataWithContentsOfURL:url];
    
    //转换二进制数据
    self.image1 = [UIImage imageWithData:imageData];

});

//将队列添加到队列组中,执行任务(下载图2)
dispatch_group_async(group, queue, ^{
    
    //确定图片地址
    NSURL * url = [NSURL URLWithString:@"http://pic.yesky.com/uploadImages/2016/126/00/7HLRG65LQ5FJ.jpg"];
    
    //根据图片地址下载二进制数据
    NSData * imageData = [NSData dataWithContentsOfURL:url];
    
    //转换二进制数据
    self.image2 = [UIImage imageWithData:imageData];
    
});


//当队列组中的任务完成之后会进入这个方法

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    //开启图片上下文
    UIGraphicsBeginImageContext(CGSizeMake(200, 200));
    
    //绘制图片到上下文中
    [self.image1 drawInRect:CGRectMake(0, 0, 100, 200)];
    [self.image2 drawInRect:CGRectMake(100, 0, 100, 200)];
    
    //获得新图片
    UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //关闭图片上下文
    UIGraphicsEndImageContext();
    
    //回到主线程,刷新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        
         // 设置图片
        self.image.image = newImage;
        
    });
    
});

- 这个方法内部并不是阻塞,内部本身是异步的
![方法内部](http:https://img.haomeiwen.com/i1404354/100b4d4670a1fc69.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 这个方法是阻塞的,会等之前的任务执行完成之后才能执行
![等待](http:https://img.haomeiwen.com/i1404354/c561530c74eb514e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 关于队列组的另一种写法

//获得全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

//创建队列组
dispatch_group_t group = dispatch_group_create();

//在该方法后面的异步任务会被纳入监听范围,进入队列组
dispatch_group_enter(group);

dispatch_async(queue, ^{
    
    NSLog(@"%@",[NSThread currentThread]);
    
    //任务执行完成之后离开队列组
    dispatch_group_leave(group);
});


dispatch_group_enter(group);

dispatch_async(queue, ^{
    
    NSLog(@"%@",[NSThread currentThread]);
    
    dispatch_group_leave(group);
    
});

- 两个异步函数的方法的区别

      1. 一个用block块封装任务
      2.一个用函数来进行任务的封装

![两个方法的区别](http:https://img.haomeiwen.com/i1404354/dcf9193766c33fbf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 全局并发队列和手动创建的并发队列的区别

  - 全局并发队列在GCD中本身就存在的,而手动创建的并发队列是重新创建的
  - 在使用栅栏函数的时候,必须要使用手动创建的并发队列,这样才能有效果
  - 在iOS6以前,GCD中只要带有了Creat和retain函数,在最后都要进行一次release操作。但是现在GCD已经被纳入ARC管理范围,已经不需要我们再进行手动release操作了。

相关文章

  • 多线程介绍

    一、进程与线程 进程介绍 线程介绍 线程的串行 二、多线程 多线程介绍 多线程原理 多线程的优缺点 多线程优点: ...

  • 多线程编程

    多线程编程之Linux环境下的多线程(一)多线程编程之Linux环境下的多线程(二)多线程编程之Linux环境下的...

  • 多线程

    创建一个多线程 创建多线程-继承线程类 创建多线程-实现Runnable接口 创建多线程-匿名类code

  • 带你搞懂Java多线程(五)

    带你搞懂Java多线程(一)带你搞懂Java多线程(二)带你搞懂Java多线程(三)带你搞懂Java多线程(四) ...

  • iOS多线程 NSOperation

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程 pthread、NSThread

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程: GCD

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程运用

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程基础

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS 多线程简介

    一.本文介绍点 1.为什么要学习多线程2.什么是多线程3.多线程的原理4.多线程的优缺点5.多线程的应用6.多线程...

网友评论

    本文标题:多线程(一)

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