zoukankan      html  css  js  c++  java
  • 2.多线程-GCD

    1.基本概念

    同步任务:在当前线程按顺序执行,不开启新的线程
    异步任务:有开新线程的欲望
     
    串行队列:一个一个执行
    并行队列:多个任务同时执行
    ---------------------------------------------------------
    串行队列-同步任务 在当前线程执行
    串行队列-异步任务  开一个子线程执行
    /**
        串行队列
     
    */
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        //串行队列,同步方法
        [self serailSync];
        
        
        
        //串行队列,异步方法
    //    [self serailAsync];
    }
    
    
    /**
     串行队列,异步方法
     
     1.打印顺序 : 从上到下依次执行,它是串行队列
     
     2.在哪条线程上执行:在子线程,因为它是异步执行,异步,就是不在当前线程里面执行
     
     
     应用场景:耗时间,有顺序的任务
     
        1.登录--->2.付费--->3.才能看
     
     */
    - (void)serailAsync{
        //1.创建一个串行队列
        /**
         参数1:队列的标识fuhao,一般是公司的域名倒写
         www.itheima.com
         参数2:队列的类型
         
         DISPATCH_QUEUE_SERIAL 串行队列
         DISPATCH_QUEUE_CONCURRENT 并发队列
         */
        dispatch_queue_t serialQueue = dispatch_queue_create("com.itheima.queue1", DISPATCH_QUEUE_SERIAL);
        
        //2.创建三个任务
        void  (^task1)  () = ^(){
            NSLog(@"task1---%@",[NSThread currentThread]);
        };
        
        void  (^task2)   () = ^(){
            NSLog(@"task2---%@",[NSThread currentThread]);
        };
        
        void  (^task3)   () = ^(){
            NSLog(@"task3---%@",[NSThread currentThread]);
        };
        
        //3.把我们上面创建好的三个任务,添加到队列中去,这个队列就会自己开始调用我们的任务
        //异步方法执行
        dispatch_async(serialQueue, task1);
        dispatch_async(serialQueue, task2);
        dispatch_async(serialQueue, task3);
    }
    
    /**
        串行队列,同步方法
     
        1.打印顺序 : 从上到下,依次打印,因为串行的
     
        2.在哪条线程上执行:主线程,因为是同步方法,所以在当前线程里面执行,恰好当前线程是主线程,所以它就在主线程上面执行
     
     
        应用场景:开发中很少用
     
     */
    
    - (void)serailSync{
        //1.创建一个串行队列
        /**
            参数1:队列的标识fuhao,一般是公司的域名倒写
            www.itheima.com
            参数2:队列的类型
         
                DISPATCH_QUEUE_SERIAL 串行队列
                DISPATCH_QUEUE_CONCURRENT 并发队列
         */
        dispatch_queue_t serialQueue = dispatch_queue_create("com.itheima.queue1", DISPATCH_QUEUE_SERIAL);
        
        //2.创建三个任务
        void  (^task1)  () = ^(){
            NSLog(@"task1---%@",[NSThread currentThread]);
        };
        
        void  (^task2)   () = ^(){
            NSLog(@"task2---%@",[NSThread currentThread]);
        };
        
        void  (^task3)   () = ^(){
            NSLog(@"task3---%@",[NSThread currentThread]);
        };
        
        //3.把我们上面创建好的三个任务,添加到队列中去,这个队列就会自己开始调用我们的任务
        //同步方法执行
        dispatch_sync(serialQueue, task1);
        dispatch_sync(serialQueue, task2);
        dispatch_sync(serialQueue, task3);
    }
    View Code
    并行队列-同步任务 在当前线程执行
    并行队列-异步任务 开多个子线程执行 
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        //1.并发队列,同步任务
        //[self concurrentSync];
        
        //2.并发队列,异步任务
        [self concurrentAsync];
    }
    
    - (void)concurrentAsync{
        /**
         并发队列,异步方法
         
         1.打印顺序 :无序的
         
         2.在哪条线程上执行:在子线程上执行,第一个任务,都在它自己的线程上执行
                开N条,它是由底层可调度线程池来决定的,可调度线程池它是有一个重用机制
         
         应用场景:
            半月转
         开头  中间  结尾
         
         下载电视剧某一集的时候,可以把我们的片头,片尾,中间内容 一起下
         
         最后,拼接组合一下,就可以播放了
         
         片头,中间内容,片尾
         
         */
        //1.创建一个并发的队列
        dispatch_queue_t concurrentQueue = dispatch_queue_create("com.itheima.queue2", DISPATCH_QUEUE_CONCURRENT);
        
        //2.创建三个任务
        void  (^task1)  () = ^(){
            NSLog(@"task1---%@",[NSThread currentThread]);
        };
        
        void  (^task2)   () = ^(){
            NSLog(@"task2---%@",[NSThread currentThread]);
        };
        
        void  (^task3)   () = ^(){
            NSLog(@"task3---%@",[NSThread currentThread]);
        };
        
        
        //3.将我们的三个任务,添加到队列中
        //异步方法去执行
        dispatch_async(concurrentQueue, task1);
        dispatch_async(concurrentQueue, task2);
        dispatch_async(concurrentQueue, task3);
    }
    
    /**
     并发队列,同步方法
     
     1.打印顺序 : 依次执行,因为它是同步的
     
     2.在哪条线程上执行:主线程,因为它是同步方法,它就在当前线程里面执行,主线程,依次执行
     
     当它遇到同步的时候,并发队列,还是依次执行
     所以说,方法的优先级会比队列的优先级高
     
     * 只要是同步方法,都只会在当前线程里面执行,不会开子线程
     
     应用场景:
        开发中几乎不用
     
     */
    - (void)concurrentSync{
        //1.创建一个并发的队列
        dispatch_queue_t concurrentQueue = dispatch_queue_create("com.itheima.queue2", DISPATCH_QUEUE_CONCURRENT);
        
        //2.创建三个任务
        void  (^task1)  () = ^(){
            NSLog(@"task1---%@",[NSThread currentThread]);
        };
        
        void  (^task2)   () = ^(){
            NSLog(@"task2---%@",[NSThread currentThread]);
        };
        
        void  (^task3)   () = ^(){
            NSLog(@"task3---%@",[NSThread currentThread]);
        };
    
        
        //3.将我们的三个任务,添加到队列中
        //同步方法去执行
        dispatch_sync(concurrentQueue, task1);
        dispatch_sync(concurrentQueue, task2);
        dispatch_sync(concurrentQueue, task3);
    }
    View Code
    全局队列不需要创建,直接get就能用,执行效果与并行队列相同
     
    主线程队列,保证操作在主线程上执行
    异步任务,顺序执行,会先执行后面的代码
     
    主线程队列中存在同步任务,永远不会被执行,被阻塞了
     主队列只有在主线程空闲的时候,才会去调度它里面的任务去执行,死等
     
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        //1.主队列,异步
        [self mainAsync];
        
        //2.主队列,同步
        //[self mainSync];
    }
    
    /**
        主队列,同步任务有问题,不能用
     
        `主队列`只有在`主线程空闲`的时候,才会去调度它里面的任务去执行
     
     */
    - (void)mainSync{
        //只是用来调试,说明我们来到了这个方法
        NSLog(@"%s",__func__);
        
        //1.获取主队列
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
        //2.搞三个任务
        void (^task1)  () = ^(){
            NSLog(@"task1===%@",[NSThread currentThread]);
        };
        
        void (^task2)  () = ^(){
            NSLog(@"task2===%@",[NSThread currentThread]);
        };
        
        void (^task3)  () = ^(){
            NSLog(@"task3===%@",[NSThread currentThread]);
        };
        
        //3.将任务添加到主队列中
        dispatch_sync(mainQueue, task1);
        dispatch_sync(mainQueue, task2);
        dispatch_sync(mainQueue, task3);
        
        NSLog(@"mainSync----end");
    }
    
    /**
        主队列,异步任务
        1.执行顺序:依次执行,因为它在主线程里面执行
        * 似乎与我们的异步任务有所冲突,但是因为它是主队列,所以,只在主线程里面执行
     
        2.是否会开线程:不会开,因为它在主线程里面执行
     
        应用场景:
            当做了耗时间操作之后,当我们需要回到主线程更新UI的时候,就非它不可
     */
    - (void)mainAsync{
        //1.获取主队列
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
        //2.搞三个任务
        void (^task1)  () = ^(){
            NSLog(@"task1===%@",[NSThread currentThread]);
        };
        
        void (^task2)  () = ^(){
            NSLog(@"task2===%@",[NSThread currentThread]);
        };
        
        void (^task3)  () = ^(){
            NSLog(@"task3===%@",[NSThread currentThread]);
        };
        
        //3.将任务添加到主队列中
        dispatch_async(mainQueue, task1);
        dispatch_async(mainQueue, task2);
        dispatch_async(mainQueue, task3);
        
        NSLog(@"---mainAsync---end");
    }
    View Code

    2.常用代码

    /**
            如果要在子线程中执行
         
            除了`主队列`都可以,串行,并发,全局都可以
         
            去主线程只有一个方法,那就是主队列,异步
         */
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //1.去子线程里面加载图片
            
            NSLog(@"假装在下载...%@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:3.0];
            
            //2.去主线程更新UI
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"%@----更新UI",[NSThread currentThread]);
            });
        });

    3.同步的作用

    /**
        同步的作用:保证我们任务执行的先后顺序
     
        1.登录
     
        2.同时下载三部小电影
     */
    - (void)execLongTimeOperation{
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //1.登录,同步在当前线程里面工作
            dispatch_sync(dispatch_get_global_queue(0, 0), ^{
                NSLog(@"%@----登录...",[NSThread currentThread]);
                
                [NSThread sleepForTimeInterval:3.0];
            });
            
            //2.同时下载三部小电影(不需要先后顺序)
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                NSLog(@"downLoadA----%@",[NSThread currentThread]);
            });
            
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                NSLog(@"downLoadV----%@",[NSThread currentThread]);
            });
            
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                NSLog(@"downLoadI----%@",[NSThread currentThread]);
            });
        });
    }

    4.延迟操作

    /**
            参数1.延时多少纳秒,整个延迟3秒
            参数2:是决定,参加在哪个线程里面调用
            参数3:任务执行的代码块
         
         
            dispatch_after 异步的
         
            应用场景:
                动画
         
         */
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"---下载完毕---%@",[NSThread currentThread]);
        });
        
        NSLog(@"---end---");

    5.单例

    + (instancetype)sharedNetWorkToos;
    static id _instance;
    + (instancetype)sharedNetWorkToos{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [[self alloc] init];
        });
        
        return _instance;
    }
    
    + (instancetype)allocWithZone:(struct _NSZone *)zone{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [super allocWithZone:zone];
        });
        
        return _instance;
    }
    
    - (id)copyWithZone:(NSZone *)zone{
        return _instance;
    }

    6.调度组,组内的线程全部执行完,会到dispatch_group_notify定义的方法中

    //1.创建一个调度组
        dispatch_group_t group = dispatch_group_create();
        
        //2.获取全局队列
        dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
        
        //3.三个下载任务
        void (^task1) () = ^(){
            NSLog(@"%@----下载片头",[NSThread currentThread]);
        };
        
        void (^task2) () = ^(){
            NSLog(@"%@----下载中间的内容",[NSThread currentThread]);
            
            [NSThread sleepForTimeInterval:5.0];
            
            NSLog(@"--下载中间内容完毕---");
        };
        
        void (^task3) () = ^(){
            NSLog(@"%@----下载片尾",[NSThread currentThread]);
        };
        
        //3.需要将我们的队列和任务,加入组内去监控
        dispatch_group_async(group, globalQueue, task1);
        dispatch_group_async(group, globalQueue, task2);
        dispatch_group_async(group, globalQueue, task3);
        
        //4.监听的函数
        /**
            参数1:组
            参数2:参数3在哪个线程里面执行
            参数3:组内完全下载完毕之后,需要执行的代码
         */
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
           //表示组内的所有任务都完成之后,会来到这里
            
            NSLog(@"把下好的视频按照顺序拼接好,然后显示在UI去播放");
        });

    调度组的实现原理

    /**
        应用场景
        比如我去同时开三个线程下载视频,只有当三个视频完全下载完毕之后,我才能做后续的事,
        这个就需要用到调度组,这个调度组,就能监听它里面的任务是否都执行完毕
     */
    - (void)execGroupDispatch{
        //1.创建一个调度组
        dispatch_group_t group = dispatch_group_create();
        
        //2.获取全局队列
        dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
        
        //3.三个下载任务
        
        dispatch_group_enter(group); //引用计数+1
        void (^task1) () = ^(){
            NSLog(@"%@----下载片头",[NSThread currentThread]);
            
            dispatch_group_leave(group); //引用计数-1
        };
        
        dispatch_group_enter(group); //引用计数+1
        void (^task2) () = ^(){
            NSLog(@"%@----下载中间的内容",[NSThread currentThread]);
            
            [NSThread sleepForTimeInterval:5.0];
            
            NSLog(@"--下载中间内容完毕---");
            dispatch_group_leave(group);//引用计数-1
        };
        
        void (^task3) () = ^(){
            NSLog(@"%@----下载片尾",[NSThread currentThread]);
        };
        
        //3.需要将我们的队列和任务,加入组内去监控
        dispatch_group_async(group, globalQueue, task1);
        dispatch_group_async(group, globalQueue, task2);
        dispatch_group_async(group, globalQueue, task3);
        
        //4.监听的函数
        /**
            参数1:组
            参数2:参数3在哪个线程里面执行
            参数3:组内完全下载完毕之后,需要执行的代码
         */
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
           //表示组内的所有任务都完成之后,会来到这里
            
            NSLog(@"把下好的视频按照顺序拼接好,然后显示在UI去播放");
        });
    }
    View Code

    7.定时器与运行循环

    @interface ViewController ()
    
    @property (nonatomic, assign) int count;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        
        
        [self performSelectorInBackground:@selector(subThreadRun) withObject:nil];
        
    }
    
    - (void)subThreadRun{
        NSLog(@"%s----%@",__func__,[NSThread currentThread]);
        
        //1.定义了一个定时器
        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES];
        
        //2.将我们的定时器加入到运行循环,只有加入到当前的运行循环里面去,他才知道你这个时候,有一个定时任务
        /**
         NSDefaultRunLoopMode 当拖动的时候,它会停掉
         因为这种模式是互斥的
         
         forMode:UITrackingRunLoopMode 只有输入的时候,它才会去执行定时器任务
         
         NSRunLoopCommonModes 包含了前面两种
         */
        //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
        //[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
        
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        
        //下载,定时源事件,输入源事件,如果放在子线程里面,如果想要它执行任务,就必须开启子线程的运行循环
        CFRunLoopRun();
    }
    
    - (void)timeEvent{
        self.count ++;
        
        NSLog(@"%d",self.count);
        
        if (self.count==5) {
            NSLog(@"---guale---");
            //停止当前的运行循环
            CFRunLoopStop(CFRunLoopGetCurrent());
        }
    }
  • 相关阅读:
    linux 进入 GNOME X 界面
    POJ 3436 ACM Computer Factory (拆点+最大流)
    学习:EF(Entity Framwork)结构【转】
    .net上传Excel,解析Excel,导出CSV
    在Handler里面使用Session
    如何用JavaScript判断访问的来源是电脑还是手机,是什么浏览器?
    ASP.NET List泛型分页代码 【转】
    ASP.NET MVC中实现多个按钮提交的几种方法【转】
    清空数据库所有表,所有存储过程SQL语句
    可以不被浏览器拦截的弹出窗口
  • 原文地址:https://www.cnblogs.com/fanglove/p/5215327.html
Copyright © 2011-2022 走看看