• iOS-GCD多线程


    GCD

    GCD —— Grand Central Dispatch

    • 是基于C语言的底层API
    • 用Block定义任务,将任务添加到线程中使用。集中管理

    1、GCD的执行函数

    //同步
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
    
    • [x]queue:队列:用来存放任务
    • [x]block:任务:要执行的操作
    //异步
    dispatch_async(dispatch_deququ_t queue, dispatch_block_t block);
    
    • [x]同步:在当前线程中执行,不具备创建线程的功能
    • [x]异步:在新的线程中执行,具备开启新线程的功能
    全局并发队列 手动创建串行队列 主队列
    同步(sync) 不开线程串行 不开线程串行 不开线程串行
    异步(async) 可能开线程并行 开线程串行 不开线程串行
    /*
     同步 + 主队列 = 需要记住的就一点: 同步函数不能搭配主队列使用
     注意: 如果是在子线程中调用同步函数 + 主对列 是可以执行的
     */
    

    2、GCD队列

    并发队列

    不用手动创建,直接获取全局的并发队列就可以

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORUTY_DEFAULT,0);
    

    其中,参数0是预留参数,现在没用。

    串行队列

    获取串行队列

    • 方式一:手动创建
    /**
         *  创建一个串行队列
         *
         *  @param label#> 队列的标示字符串 description#>
         *  @param attr#>  初始化队列的属性 description#>
         *
         *  @return 创建好得串行队列
         */
        dispatch_queue_t queue = dispatch_queue_create("xyl.queue", NULL);
    
        //非ARC下需要手动释放创建的队列
        dispatch_release(queue);
    
    • 方式二:获得主队列

    获取GCD主队列,主队列是和串行的和主线程相关的队列。

    主队列都是串行

    // 如果任务放在主队列中, 哪怕是异步方法也不会创建新的线程
    dispatch_get_main_queue();
    

    3、前面1和2结合起来的例子:

    /*
     异步 +  并行 = 会开启新的线程
     */
    
        //获取全局队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        //将任务添加到全局的异步队列中执行
        dispatch_async(queue, ^{
            NSLog(@"任务%@", [NSThread mainThread]);
        });
    
    /*
     异步 + 串行 = 会创建新的线程, 但是只会创建一个新的线程, 所有的任务都在这一个新的线程中执行
     异步任务, 会先执行完所有的代码, 再在子线程中执行任务
     */
    
        dispatch_queue_t queue = dispatch_queue_create("xyl.queue", NULL);
        dispatch_async(queue, ^{
            NSLog(@"-----download-----%@",[NSThread mainThread]);
        });
    
    参数:
    • DISPATCH_QUEUE_SERIAL (or NULL) 串行队列

    • DISPATCH_QUEUE_CONCURRENT 并行

    4、GCD的线程之间通讯

    如果是通过异步函数调用, 那么会先执行完所有的代码, 再更新UI

    如果是同步函数调用, 那么会先更新UI, 再执行其它代码

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{
            //子线程中下载图片
            NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
    
            dispatch_queue_t mainQueue = dispatch_get_main_queue();
            // 如果是通过异步函数调用, 那么会先执行完所有的代码, 再更新UI
            // 如果是同步函数调用, 那么会先更新UI, 再执行其它代码
            dispatch_async(mainQueue, ^{
                //主线程拿到图片进行设置
                [self.button setImage:image forStatus:UIControlStateNormal];
            });
        });
    

    5、延迟执行

    在程序中,我们经常会遇到希望间隔一段时间再做某件事,下面我们来看看几种方法的实现:

    • 睡眠方式(缺点明显:阻塞当前线程)
    // 延迟执行不要用sleep,坏处:卡住当前线程
    [NSThread sleepForTimeInterval:3];
    
    • 定制延迟任务
    // 一旦定制好延迟任务后,不会卡主当前线程
    [self performSelector:@selector(download:) withObject:@"http://555.jpg" afterDelay:3];
    

    这种方法可以很好的实现需要的延迟功能,但是只能在当前线程中执行,不能灵活的控制执行的线程

    • GCD方式
    // 3秒后回到主线程执行block中的代码
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), mainQueue, ^{
    
            NSLog(@"------task------%@", [NSThread currentThread]);
    
    });
    
    // 3秒后自动开启新线程 执行block中的代码
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{
    
        NSLog(@"------task------%@", [NSThread currentThread]);
    
    });
    

    GCD方式,既可以实现延迟执行,也可以实现控制任务在不同的线程中执行,方便好用。

    6、GCD代码运行时只执行一次

    在程序运行的过程中,经常会遇到希望某段代码只执行一次。我们除了可以使用设置一个BOOL变量控制以外,还能使用GCD方式,简单方便

    static dispatch_once_t onoceToken;
    dispatch_once(&onceToken, ^{
        //执行一次的代码
    });
    

    快捷方式:上面的代码块只写dispatch_once就会全部出现。

    7、队列组

    如果想要多个并发的线程事件完成之后,再执行某一个事件,就可以使用队列组,将先执行的多个事件线程放在队列组里面先执行。例如:将多个图片下载完成后,再使用图片的时候,如果不用队列组,会很麻烦。

    举例:

    
        // 1.队列组
        dispatch_group_t group = dispatch_group_create();
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
        // 2.下载图片1
        __block UIImage *image1 = nil;
        dispatch_group_async(group, queue, ^{
            NSURL *url1 = [NSURL URLWithString:@"http://g.hiphotos.baidu.com/image/pic/item/f2deb48f8c5494ee460de6182ff5e0fe99257e80.jpg"];
            NSData *data1 = [NSData dataWithContentsOfURL:url1];
            image1 = [UIImage imageWithData:data1];
        });
    
        // 3.下载图片2
        __block UIImage *image2 = nil;
        dispatch_group_async(group, queue, ^{
            NSURL *url2 = [NSURL URLWithString:@"http://su.bdimg.com/static/superplus/img/logo_white_ee663702.png"];
            NSData *data2 = [NSData dataWithContentsOfURL:url2];
            image2 = [UIImage imageWithData:data2];
        });
    
        // 4.合并图片 (保证执行完组里面的所有任务之后,再执行notify函数里面的block)
        dispatch_group_notify(group, queue, ^{
            // 开启一个位图上下文
            UIGraphicsBeginImageContextWithOptions(image1.size, NO, 0.0);
    
            // 绘制第1张图片
            CGFloat image1W = image1.size.width;
            CGFloat image1H = image1.size.height;
            [image1 drawInRect:CGRectMake(0, 0, image1W, image1H)];
    
            // 绘制第2张图片
            CGFloat image2W = image2.size.width * 0.5;
            CGFloat image2H = image2.size.height * 0.5;
            CGFloat image2Y = image1H - image2H;
            [image2 drawInRect:CGRectMake(0, image2Y, image2W, image2H)];
    
            // 得到上下文中的图片
            UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
    
            // 结束上下文
            UIGraphicsEndImageContext();
    
            // 5.回到主线程显示图片
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageView.image = fullImage;
            });
        });
    

    8、栅栏

    dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
    

    在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行
    这个queue不能是全局的并发队列

    • 如果所有的任务都在"同一个"队列中,f 那么在barrier方法之前添加的任务会先被执行, 只有等barrier方法之前添加的任务执行完毕, 才会执行barrier

    • 而且如果是在barrier方法之后添加的任务, 必须等barrier方法执行完毕之后才会开始执行

    • 要想执行完前面所有的任务再执行barrier必须满足两个条件

      • 1、 所有任务都是在同一个队列中
      • 2、 队列不能是全局并行队列, 必须是自己创建的队列
    dispatch_barrier_async(queue, ^{
    }
    

    9、GCD快速迭代

    /*
         第一个参数: 需要执行几次任务
         第二个参数: 队列
         第三个参数: 当前被执行到得任务的索引
         */
    
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
         NSLog(@"%@, %zd",[NSThread currentThread] , index);
         });
    

    例子:将一个文件夹中的文件,拷贝到另一个文件夹

        // 1.获取images文件夹中所有的文件
        NSString *sourcePath = @"/Users/apple/Desktop/abc";
        NSString *dest = @"/Users/apple/Desktop/lnj";
    
        // 2.获取images文件夹中所有的文件
        NSFileManager *mgr = [NSFileManager defaultManager];
        NSArray *subPaths = [mgr subpathsAtPath:sourcePath];
    
        // 3.剪切文件到lnj文件夹中
    
        CFAbsoluteTime begin = CFAbsoluteTimeGetCurrent();
        dispatch_apply(4, dispatch_get_global_queue(0, 0), ^(size_t index) {
    
            // 3.1获取当前遍历到得文件的名称
            NSString *fileNmae = subPaths[index];
            // 3.2根据当前文件的名称, 拼接全路径
            NSString *fromPath = [sourcePath stringByAppendingPathComponent:fileNmae];
            NSString *toPath = [dest stringByAppendingPathComponent:fileNmae];
            NSLog(@"fromPath = %@", fromPath);
            NSLog(@"toPath = %@", toPath);
            NSLog(@"ddd");
            [mgr moveItemAtPath:fromPath toPath:toPath error:nil];
    
        });
        CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    
    
    
  • 相关阅读:
    vue插件(还真是第一次接触)
    Vue父组件向子组件传值以及data和props的区别
    Vue v-bind与v-model的区别
    vue 异步渲染
    vue动态加载不同的组件(分内部和外部组件)
    vue自定义组件的递归
    作用域插槽模板迭代的次数,取决于组件内部独立slot的数量
    说说 Vue.js 中的 v-cloak 指令
    C语言 system
    C语言 有符号、无符号
  • 原文地址:https://www.cnblogs.com/66it/p/4719721.html
Copyright © 2020-2023  润新知