面试移动开发寒哥管理的技术专题

多线程(一)

2016-05-21  本文已影响80人  Shawn_

基本概念

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

5.多线程的优缺点

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

- pthread
- NSThread
- GCD
- NSOperation

NSThread

GCD

 //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_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操作了。
上一篇下一篇

猜你喜欢

热点阅读