多线程

作者: 七里田间的守望者 | 来源:发表于2016-11-23 14:55 被阅读14次

    多线程原理:

    • 同一时间CPU只能处理一条线程, 只有一条线程在工作
    • 多线程并发执行,起始是CPU在各个线程之间快速调度的结果
    • 由于CPU调度线程速度非常快,所以就造成了多线程并发的假象

    一般情况下耗时操作放在子线程里面,多线程也正是解决耗时操作,防止卡住主线程产生的。

    主线程

    • 程序已启动就自动创建的线程就是主线程
    • 作用一般就是相应用户点击事件,刷新UI等等

    多线程的实现方案

    Paste_Image.png
    • NSThread
    @Parmark 第一中创建方法
        //创建线程
        //在内存中开辟空间
        NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"thread"];
        thread.name = @"my-thread";
        //启动线程  并且系统会把这个线程放到可调度程序池里面 为了方便CPU来回调用
        [thread start];
    
    //任务执行完毕后 会自动销毁线程
    - (void)run:(NSString *)param
    {
        //处理耗时操作
    }
    
    @Parmark 第二中创建方法
    
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"thread"];
    
    - (void)run:(NSString *)param
    {
        //处理耗时操作
    }
    
    @Parmark 第三中创建方法
    
    [self performSelectorInBackground:@selector(run:) withObject:@"thread"];
    
    - (void)run:(NSString *)param
    {
        //处理耗时操作
    }
    
    
     //卡住线程睡两秒 控制线程进入阻塞状态
    [NSThread sleepForTimeInterval:2.0];
    
    
    //退出线程(强制性的)
    [NSThread exit];
    
    

    线程安全

    • 资源共享:一块资源 被多个线程共享 也就是多个线程访问同一块资源

    隐患:

    Paste_Image.png

    解决引号 -- > 加把互斥锁

    Paste_Image.png

    代码:

    ###没加互斥锁的代码
    
        self.ticketCount = 100;//100张票
        
        self.thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
        self.thread1.name = @"售票员1";
        
        self.thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
        self.thread2.name = @"售票员2";
        
        self.thread3 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
        self.thread3.name = @"售票员3";
    
    - (void)saleTicket
    {
        while (1) {
            //先取出总数
            NSInteger count = self.ticketCount;
            if (count > 0) {
                
                self.ticketCount = count - 1;
                
                NSLog(@"%@卖了一张票 -- 还剩下%ld张票",[NSThread currentThread].name,_ticketCount);
            }else{
                NSLog(@"票已经卖完了");
                break;
            }
        }
    }
    
    ###加互斥锁的代码
    - (void)saleTicket
    {
        //用同一把锁 可以记录线程的状态
        while (1) {
            
            @synchronized (self) {//加锁
                
                //先取出总数
                NSInteger count = self.ticketCount;
                if (count > 0) {
                    
                    self.ticketCount = count - 1;
                    
                    NSLog(@"%@卖了一张票 -- 还剩下%ld张票",[NSThread currentThread].name,_ticketCount);
                }else{
                    NSLog(@"票已经卖完了");
                    break;
                }
            }
            
        }
    }
    

    线程间的通讯

    
    //开辟一个子线程
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        [self performSelectorInBackground:@selector(downLoad) withObject:nil];
    }
    
    
    //子线程要做的事
    - (void)downLoad
    {
        NSURL * url = [NSURL URLWithString:@"http://f.hiphotos.baidu.com/image/pic/item/5ab5c9ea15ce36d358d27ee43ef33a87e850b114.jpg"];
        
        //下载图片
        NSData * data = [NSData dataWithContentsOfURL:url];
        
        //生成图片
        UIImage * image = [UIImage imageWithData:data];
        
        //回到主线程刷新UI
        [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
        
    //    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
    
    }
    
    

    GCD的基本使用

    • 任务: 执行操作
    • 队列: 存放任务
      • 队列的类型
      • 并发队列 (只有在)dispatch_async下才有效
      • 串行队列

    同步 - 异步:主要影响是能不能开新线程

    • 同步只能在当前线程执行任务,不能开启新线程
    • 异步可以在新的线程中执行任务,能开启新线程

    串行 - 并发:主要影响任务的执行方式

    • 串行:一个任务执行完毕 执行下一个任务
    • 并发:多个任务同时执行

    使用步骤

    • 定制任务:确定想要做的事情
    • 将任务添加到队列中
      • GCD会自动将队列中的任务取出来,放到对应的线程中执行
      • 任务的去除遵循:先进先出 后进后出 的原则

    代码:

    
    //异步线程
        dispatch_async(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)
        //同步线程
        dispatch_sync(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)
    
    ###异步函数+并发队列 (可以开启多条线程 并且可以同时执行)
    //创建一个并发队列  DISPATCH_QUEUE_CONCURRENT:队列类型
        dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_CONCURRENT);
        
        //将任务加入队列
        dispatch_async(queue, ^{
           
            NSLog(@"%@",[NSThread currentThread]);
            
        });
    
    #@pargam 或者这种写法
    
    //获取全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        //将任务加入队列
        dispatch_async(queue, ^{
           
            NSLog(@"%@",[NSThread currentThread]);
            
        });
    
    
    ###同步函数+并发队列 (不会开启新的线程)
    
    //获得全局的并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        //同步函数
        dispatch_sync(queue, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
    
    
    ###异步函数+串行对列 (可以开线程 但是不能同时执行)
    //串行队列 没有全局的 只能手动创建
        dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
        
        dispatch_async(queue, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
    
    ###同步函数+串行对列 (不可以开线程 )
    
     //串行队列 没有全局的 只能手动创建
        dispatch_queue_t queue = dispatch_queue_create("com.baidu.queue", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(queue, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
    
    

    主队列

    • GCD自带的一种特殊的串行队列
    • 放到主队列的任务都会,在主线程中执行
    • 使用dispatch_get_main_queue()获取主队列
    
    ###主队列+异步函数(只会在主线程中执行任务)
    
    dispatch_queue_t queue = dispatch_get_main_queue();
        
        dispatch_async(queue, ^{
            
            NSLog(@"%@",[NSThread currentThread]);
            
        });
    
    ###主队列+同步函数(线程冲突 不会执行任何操作)
    
    dispatch_queue_t queue = dispatch_get_main_queue();
        
        dispatch_sync(queue, ^{
            
            NSLog(@"%@",[NSThread currentThread]);
            
        });
    

    各种队列的执行效果

    Paste_Image.png

    GCD的线程之间通讯

    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
           
            NSURL * url = [NSURL URLWithString:@"https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1479864304&di=99dcf40127f2dc4273536f73d0951638&src=http://d.hiphotos.baidu.com/image/pic/item/2e2eb9389b504fc2065e2bd2e1dde71191ef6de0.jpg"];
            NSData * data = [NSData dataWithContentsOfURL:url];
            
            UIImage * image = [UIImage imageWithData:data];
            
            //回到主线程
            dispatch_async(dispatch_get_main_queue(), ^{
               
                self.imageView.image = image;
                
            });
            
        });
    
    

    GCD中还有另外一个执行任务的函数

    //在它前面的函数执行完毕才执行它的任务,在它后面的函数在它执行完毕后才开始执行
    dispatch_barrier_sync(<#dispatch_queue_t  _Nonnull queue#>, <#^(void)block#>)
    

    iOS延时执行的函数

    //延时两秒执行run方法
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            //执行的操作
            
        });
    
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
    
    
    static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            //一次性函数
            //此函数只执行一次
        });
    
    

    GCD队列组

    • 用队列组下载多张图片并且合成一张图片
    
    //创建一个队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        //创建一个队列组
        dispatch_group_t group = dispatch_group_create();
        
        //1.下载图片1
        dispatch_group_async(group, queue, ^{
            
            NSURL * url = [NSURL URLWithString:@"http://d.hiphotos.baidu.com/image/h%3D200/sign=4241e02c86025aafcc3279cbcbecab8d/562c11dfa9ec8a13f075f10cf303918fa1ecc0eb.jpg"];
            NSData * data = [NSData dataWithContentsOfURL:url];
            
            UIImage * image = [UIImage imageWithData:data];
            
            self.image1 = image;
            
            
        });
        //2.下载图片2
        dispatch_group_async(group, queue, ^{
            
            
            NSURL * url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/8cb1cb134954092359d94e479758d109b3de4952.jpg"];
            NSData * data = [NSData dataWithContentsOfURL:url];
            
            UIImage * image = [UIImage imageWithData:data];
            
            self.image2 = image;
            
            
        });
        //3.将图片1和图片2合成一张新的图片
        dispatch_group_notify(group, queue, ^{
            //能保证组里面的任务都完成了
            //能来到这里说明前两张图片一定下载完了
            
            //开启图形上下文
            UIGraphicsBeginImageContext(CGSizeMake(336, 440));
            
            //绘制图片
            [self.image1 drawInRect:CGRectMake(0, 0, 168, 220)];
            [self.image2 drawInRect:CGRectMake(168, 0, 168, 220)];
            
            //获取上下文的图片
            UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
            
            //结束上下文
            UIGraphicsEndImageContext();
            
            //回到主线程显示图片
            dispatch_async(dispatch_get_main_queue(), ^{
               
                self.imageView.image = image;
                
            });
            
            
        });
        //4.将合成后的图片显示出来
    

    GCD实现单利

    ###第一种方式
    
    static Person * _person;
    + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [super allocWithZone: zone];
        });
        return _instance;
    }
    
    + (instancetype)defaultManger
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            
            _person = [[self alloc]init];
            
        });
        return _person;
    }
    
    
    //记得遵守NSCopying协议
    //实现此方法为了保证copy的时候  访问的是同一个对象
    - (id)copyWithZone:(NSZone *)zone
    {
        return _person;
    }
    
    
    ###第二种方式
    
    static id _instance;
    + (instancetype)allocWithZone:(struct _NSZone *)zone
    {
        @synchronized (self) {//加锁 防止多线程访问出问题
            if (_instance == nil)
            {
                _instance = [self allocWithZone:zone];
            }
        }
        return _instance;
    }
    
    + (instancetype)sharedInstance
    {
        @synchronized (self) {//加锁 防止多线程访问出问题
            if (_instance == nil)
            {
                _instance = [[self alloc]init];
            }
        }
        return _instance;
    }
    
    - (id)copyWithZone:(NSZone *)zone
    {
        return _instance;
    }
    

    相关文章

      网友评论

          本文标题:多线程

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