iOS多线程编程(四)------ GCD(Grand Central Dispatch)

一、简介

是基于C语言开发的一套多线程开发机制,也是目前苹果官方推荐的多线程开发方法,用起来也最简单,只是它基于C语言开发,并不像NSOperation是面向对象的开发,而是完全面向过程的。如果使用GCD,完全由系统管理线程,我们不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的调度队列(dispatch_queue).GCD会负责创建线程和调度你的任务,系统会直接提供线程管理。

二、任务和队列

GCD中有两个核心概念

(1)任务:执行什么操作

(2)队列:用来存放任务

GCD的使用就两个步骤

(1)定制任务

(2)确定想做的事情

将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行

提示:任务的取出遵循队列的FIFO原则:先进先出,后进后出

三、执行任务

1、GCD中有2个用来执行任务的函数

说明:把右边的参数(任务)提交给左边的参数(队列)进行执行

(1)用同步的方式执行任务 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

参数说明:queue:队列; block:任务

(2)用异步的方式执行任务 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

2、同步和异步的区别

同步:在当前线程中执行

异步:在另一条线程中执行

四、队列

1、GCD的队列可以分为2大类型

(1)并发队列(Concurrent Dispatch Queue):可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数才有效

(2)串行队列(Serial Dispatch Queue):让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

2、补充说明

有4个术语比较容易混淆:同步、异步、并发、串行(在博客 多线程编程(-)—-概念 也提到了)

同步和异步决定了要不要开启新的线程

同步:在当前线程中执行任务,不具备开启新线程的能力

异步:在新的线程中执行任务,具备开启新线程的能力

并发和串行决定了任务的执行方式

并发:多个任务并发执行

串行:一个任务执行完毕后,再执行下一个任务

五、(同步/异步)串行队列和(同步/异步)并发队列开启线程的总结 (代码示例)

说明:

(1)同步函数不具备开启线程的能力,无论是什么队列都不会开启线程;异步函数具备开启线程的能力,开启几条线程有队列决定(串行队列只会开启一条新的线程,并发队列会开启多条线程)

(2) MRC下凡是函数中,各种函数名中带有create\copy\new\retain等字眼,都需要在不需要使用这个数据的时候进行release

ARC下GCD的数据类型不需要再作release

CF(core Foundation)的数据在ARC环境下还是需要release

(3) 异步函数具备开线程的能力,但不一定会开线程

1、异步并发队列(同时开启N个线程)

/**
 *  异步并发队列
 */
- (void)asynchronousConcurrent{
    NSLog(@"异步函数往并发队列中添加任务");
    NSLog(@"主线程1111 ---- %@", [NSThread currentThread]);

    // 1、创建并发队列
    // 方法一 和创建串行队列一样
    //    dispatch_queue_t queue = dispatch_queue_create("asynConcurrent", DISPATCH_QUEUE_CONCURRENT);
    // 方法二 获取全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2、添加任务到队列
    dispatch_async(queue, ^{
        NSLog(@"下载图片1 ------ %@", [NSThread currentThread]);
        [self loadImage:1];
    });

    dispatch_async(queue, ^{
        NSLog(@"下载图片2------ %@", [NSThread currentThread]);
        [self loadImage:2];
    });

    dispatch_async(queue, ^{
        NSLog(@"下载图片3 ------ %@", [NSThread currentThread]);
        [self loadImage:3];
    });

    NSLog(@"主线程2222 ---- %@", [NSThread currentThread]);

    // 打印结果 开启多个线程,并发执行。没有先后顺序(有的话也是刚好巧合而已) 可以看number 可以看做线程值
    /**
     第一次执行
     2016-08-24 12:53:24.026 YJGCDDemo[1220:24092] 异步函数往并发队列中添加任务
     2016-08-24 12:53:24.027 YJGCDDemo[1220:24092] 主线程1111 ---- <NSThread: 0x7f81bae04b90>{number = 1, name = main}
     2016-08-24 12:53:24.027 YJGCDDemo[1220:24092] 主线程2222 ---- <NSThread: 0x7f81bae04b90>{number = 1, name = main}
     2016-08-24 12:53:24.027 YJGCDDemo[1220:24126] 下载图片1 ------ <NSThread: 0x7f81baf25e90>{number = 2, name = (null)}
     2016-08-24 12:53:24.027 YJGCDDemo[1220:24128] 下载图片2------ <NSThread: 0x7f81bacc5fe0>{number = 3, name = (null)}
     2016-08-24 12:53:24.027 YJGCDDemo[1220:24127] 下载图片3 ------ <NSThread: 0x7f81baf81ac0>{number = 4, name = (null)}

     第二次执行
     2016-08-24 12:53:32.427 YJGCDDemo[1220:24092] 异步函数往并发队列中添加任务
     2016-08-24 12:53:32.427 YJGCDDemo[1220:24092] 主线程1111 ---- <NSThread: 0x7f81bae04b90>{number = 1, name = main}
     2016-08-24 12:53:32.427 YJGCDDemo[1220:24092] 主线程2222 ---- <NSThread: 0x7f81bae04b90>{number = 1, name = main}
     2016-08-24 12:53:32.427 YJGCDDemo[1220:24126] 下载图片2------ <NSThread: 0x7f81baf25e90>{number = 2, name = (null)}
     2016-08-24 12:53:32.427 YJGCDDemo[1220:24260] 下载图片3 ------ <NSThread: 0x7f81baf7f660>{number = 7, name = (null)}
     2016-08-24 12:53:32.427 YJGCDDemo[1220:24153] 下载图片1 ------ <NSThread: 0x7f81baf81ac0>{number = 6, name = (null)}

     */
}

2、异步串行队列(会开启线程,但是只开启一个线程)

/**
 *  异步串行队列
 */
- (void)asynchronousSerial{

    NSLog(@"用异步函数往串行队列中添加任务");
    NSLog(@"主线程1111 ---- %@", [NSThread currentThread]);

    //1. 创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("asynSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"下载图片1 --- %@", [NSThread currentThread]);
        [self loadImage:1];
    });

    dispatch_async(queue, ^{
        NSLog(@"下载图片2 --- %@", [NSThread currentThread]);
        [self loadImage:2];
    });

    dispatch_async(queue, ^{
        NSLog(@"下载图片3 --- %@", [NSThread currentThread]);
        [self loadImage:3];
    });

    NSLog(@"主线程2222 ---- %@", [NSThread currentThread]);

    // 打印结果:异步串行队列,会开启一个线程,顺序执行。 看运行结果也可以看出,图片是一张下载完在下载下一张的。
    /**
     2016-08-24 12:39:14.195 YJGCDDemo[942:16507] 用异步函数往串行队列中添加任务
     2016-08-24 12:39:14.196 YJGCDDemo[942:16507] 主线程1111 ---- <NSThread: 0x7f8ed1f05f90>{number = 1, name = main}
     2016-08-24 12:39:14.196 YJGCDDemo[942:16507] 主线程2222 ---- <NSThread: 0x7f8ed1f05f90>{number = 1, name = main}
     2016-08-24 12:39:14.196 YJGCDDemo[942:16622] 下载图片1 --- <NSThread: 0x7f8ed1c43c00>{number = 2, name = (null)}
     2016-08-24 12:39:14.261 YJGCDDemo[942:16622] 下载图片2 --- <NSThread: 0x7f8ed1c43c00>{number = 2, name = (null)}
     2016-08-24 12:39:14.303 YJGCDDemo[942:16622] 下载图片3 --- <NSThread: 0x7f8ed1c43c00>{number = 2, name = (null)}

     */
}

3、同步并发队列(不会开启新的线程,并发队列失去并发的功能)

/**
 *  同步并发队列
 */
- (void)synchronousConcurrent{
        NSLog(@"用同步函数往并发队列中添加任务");
        NSLog(@"主线程1111 ---- %@", [NSThread currentThread]);

        // 1.创建并发队列
        // 方式一 一般都使用这种方式获取
    //    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        // 方式二 和创建串行队列一样
        dispatch_queue_t queue = dispatch_queue_create("syncConcurrentncy", DISPATCH_QUEUE_CONCURRENT);

        // 2.加添任务到队列
        dispatch_sync(queue, ^{
            NSLog(@"下载图片1 ---- %@", [NSThread currentThread]);
            [self loadImage:1];
        });

        dispatch_sync(queue, ^{
            NSLog(@"下载图片2 ---- %@", [NSThread currentThread]);
            [self loadImage:2];
        });

        dispatch_sync(queue, ^{
            NSLog(@"下载图片3 ---- %@", [NSThread currentThread]);
            [self loadImage:3];
        });

        NSLog(@"主线程2222 ---- %@", [NSThread currentThread]);

    // 打印结果 和同步串行队列一样 这边并发队列效果失效,不会开启线程。
    /**
     2016-08-24 11:20:44.153 YJGCDDemo[43913:3002870] 用同步函数往并发队列中添加任务
     2016-08-24 11:20:44.154 YJGCDDemo[43913:3002870] 主线程1111 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}
     2016-08-24 11:20:44.154 YJGCDDemo[43913:3002870] 下载图片1 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}
     2016-08-24 11:20:44.433 YJGCDDemo[43913:3002870] 下载图片2 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}
     2016-08-24 11:20:44.475 YJGCDDemo[43913:3002870] 下载图片3 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}
     2016-08-24 11:20:44.508 YJGCDDemo[43913:3002870] 主线程2222 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}

     */
}

4、同步串行队列(不会开启新的线程)

/**
 *  同步串行队列
 */
- (void)synchronousSerial{
    NSLog(@"用同步函数往串行队列中添加任务");
    NSLog(@"主线程111----- %@", [NSThread currentThread]);

    // 1、创建串行队列 // DISPATCH_QUEUE_SERIAL 串行队列 也可以为NULL NULL时 默认是 串行队列; DISPATCH_QUEUE_CONCURRENT 并发队列
    dispatch_queue_t queue = dispatch_queue_create("syncSerial", DISPATCH_QUEUE_SERIAL);

    // 2、添加任务到队列中执行
    dispatch_sync(queue, ^{
        // 此块代码没有任何意义,只是为了体现同步串行队列的效果, 只能执行完了 才能执行面下的,
        for (int i = 0; i < 30000; i++) {
            //
            NSLog(@"%i", i);
        }
        NSLog(@"下载图片1 ---- %@", [NSThread currentThread]);
        [self loadImage:1];
    });

    dispatch_sync(queue, ^{
        for (int i = 0; i < 30000; i++) {
            //
            NSLog(@"%i", i);
        }
        NSLog(@"下载图片2 -- %@", [NSThread currentThread]);
        [self loadImage:2];
    });

    dispatch_sync(queue, ^{
        for (int i = 0; i < 30000; i++) {
            //
            NSLog(@"%i", i);
        }
        NSLog(@"下载图片3 -- %@", [NSThread currentThread]);
        [self loadImage:3];
    });

    NSLog(@"主线程222----- %@", [NSThread currentThread]);

    // 打印结果就是同步按顺序执行。 每个任务按顺序执行,不开启线程
    /**
     2016-08-24 10:52:55.049 YJGCDDemo[43413:2986818] 用同步函数往串行队列中添加任务
     2016-08-24 10:52:55.049 YJGCDDemo[43413:2986818] 主线程111----- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main}
     2016-08-24 10:52:55.050 YJGCDDemo[43413:2986818] 下载图片1 ---- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main}
     2016-08-24 10:52:55.580 YJGCDDemo[43413:2986818] 下载图片2 -- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main}
     2016-08-24 10:52:55.616 YJGCDDemo[43413:2986818] 下载图片3 -- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main}
     2016-08-24 10:52:55.644 YJGCDDemo[43413:2986818] 主线程222----- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main}

     */
}

六、常用方法 在Demo中的CommonMethodsViewCotroller类

1、后台运行

 dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // something
    });

2、主线程执行

dispatch_async(dispatch_get_main_queue(), ^{
        // something
    });

3、一次性执行 dispatch_once()

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // code to be execution once
        NSLog(@"改行代码只执行一次");
    });

4、延迟N秒执行 (这边列举了四种方式) dispatch_time()

    NSLog(@"打印线程----- %@", [NSThread currentThread]);
    // 延时执行方式一 使用NSObject的方法
    // 2秒后再调用self的run方法
//    [self performSelector:@selector(loadImage) withObject:nil afterDelay:2.0];

    // 延迟执行方式二 使用GCD函数
       // 在同步函数中执行
        // 注意 如果使用异步函数 dispatch_async 那么[self performSelector:@selector(loadImage) withObject:nil afterDelay:5.0]; 不会被执行
//    dispatch_queue_t queue = dispatch_queue_create("yangjian.net.cn", 0);
//    dispatch_sync(queue, ^{
//        [self performSelector:@selector(loadImage) withObject:nil afterDelay:2.0];
//    });

    // 延迟执行方式三 可以安排其线程---> 主队列
//    dispatch_queue_t queue = dispatch_get_main_queue();
//    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
//        NSLog(@"主队列--延迟执行------%@",[NSThread currentThread]);
//        [self gcdLoadImage];
//    });

    // 延迟执行方式四 可以安排其线程---> 并发队列
    //1、获取全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //2、计算任务执行的时间
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
    //3、会在when这个时间点,执行queue中的这个任务
    dispatch_after(when, queue, ^{
        NSLog(@"并发队列--延迟执行 ---- %@", [NSThread currentThread]);
        [self gcdLoadImage];
    });

5、执行某个代码片段N次 dispatch_apply()

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(4, globalQueue, ^(size_t index) {
        // 执行4次
    });

6、队列组的使用 dispatch_group_async()

    // 需求
    //1  分别异步执行2个耗时的操作
    //2  等两个异步操作都执行完毕后,再回到主线程执行操作

//   如果想要快速高效地实现上述需求,可以考虑用队列组

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        // 并发执行的线程一
    });

    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        // 并发执行的线程二
    });

    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        // 等前面的异步操作都执行完毕后,回到主线程
    });

6、dispatch_barrier_async()

/**
 *   使用此方法创建的任务首先会查看队列中有没有别的任务要执行,如果有,则会等待已有任务执行完毕再执行;同时在此方法后添加的任务必须等待此方法中任务执行后才能执行。(利用这个方法可以控制执行顺序,例如前面先加载最后一张图片的需求就可以先使用这个方法将最后一张图片加载的操作添加到队列,然后调用dispatch_async()添加其他图片加载任务)
 */
- (void)barrier{
//    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t queue = dispatch_queue_create("RICHARD", DISPATCH_QUEUE_CONCURRENT); // 创建并发队列

    dispatch_async(queue, ^{
        NSLog(@"下载图片1 --- %@", [NSThread currentThread]);
        UIImage *image = [self requestImageData:@"http://atth.eduu.com/album/201203/12/1475134_1331559643qMzc.jpg"];

        // 回到主线程
        dispatch_queue_t mainQueue1 = dispatch_get_main_queue();
        dispatch_async(mainQueue1, ^{
            self.imageViewOne.image = image;
        });
    });

    dispatch_async(queue, ^{
        NSLog(@"下载图片2 --- %@", [NSThread currentThread]);
    });

    dispatch_barrier_async(queue, ^{
        NSLog(@"dispatch_barrier_async 下载图片3  --- %@", [NSThread currentThread]);
        UIImage *image = [self requestImageData:@"http://5.26923.com/download/pic/000/335/06efd7b7d40328f1470d4fd99a214243.jpg"];
        dispatch_async(main_queue, ^{
            self.imageViewThree.image = image;
        });
    });

    dispatch_async(queue, ^{
        NSLog(@"下载图片4 --- %@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        NSLog(@"下载图片5 --- %@", [NSThread currentThread]);
        UIImage *image = [self requestImageData:@"http://h.hiphotos.baidu.com/image/pic/item/dc54564e9258d109a4d1165ad558ccbf6c814d23.jpg"];
        dispatch_async(main_queue, ^{
            self.imageViewTwo.image = image;
        });
    });

    // 打印结果分析:1、 12 执行玩完 执行3  再执行45  2、 12顺序不定 45顺序不定
    /**
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367515] 下载图片2 --- <NSThread: 0x7ff720f13ac0>{number = 47, name = (null)}
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367518] 下载图片1 --- <NSThread: 0x7ff720e1c5d0>{number = 48, name = (null)}
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367102] dispatch_barrier_async 下载图片3  --- <NSThread: 0x7ff720d9b7d0>{number = 43, name = (null)}
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367293] 下载图片4 --- <NSThread: 0x7ff720c9e250>{number = 45, name = (null)}
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367516] 下载图片5 --- <NSThread: 0x7ff720dba290>{number = 46, name = (null)}

     */

}

七、代码示例 在Demo中的CommonMethodsViewCotroller类

下载两张照片完后,合并照片。(两种方式)

/**
 *  合并图片(方式一)
 */
- (void)mergeImage{

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

    dispatch_async(global_queue, ^{
        // 下载图片1
        UIImage *image1 = [self requestImageData:@"http://h.hiphotos.baidu.com/image/pic/item/dc54564e9258d109a4d1165ad558ccbf6c814d23.jpg"];
        NSLog(@"图片1下载完成----%@", [NSThread currentThread]);

        UIImage *image2 = [self requestImageData:@"http://5.26923.com/download/pic/000/335/06efd7b7d40328f1470d4fd99a214243.jpg"];

        NSLog(@"图片2下载完成----%@", [NSThread currentThread]);

        // 回到主线程显示图片
        dispatch_async(main_queue, ^{
            NSLog(@"显示图片---%@", [NSThread currentThread]);
            self.imageViewOne.image = image1;
            self.imageViewTwo.image = image2;

            // 合并两张图片
            UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0.0);
            [image1 drawAsPatternInRect:CGRectMake(0, 0, 100, 200)];
            [image2 drawAsPatternInRect:CGRectMake(100, 0, 100, 200)];
            self.imageViewThree.image = UIGraphicsGetImageFromCurrentImageContext();
            // 关闭上下文
            UIGraphicsEndImageContext();

            NSLog(@"图片合并完成----%@", [NSThread currentThread]);

        });
    });

    // 打印结果 需要等图片1下载完 在下载图片2 再回到主线程
    /**
     2016-08-24 16:58:40.123 YJGCDDemo[3480:125975] 图片1下载完成----<NSThread: 0x7ff65acd85f0>{number = 3, name = (null)}
     2016-08-24 16:58:40.319 YJGCDDemo[3480:125975] 图片2下载完成----<NSThread: 0x7ff65acd85f0>{number = 3, name = (null)}
     2016-08-24 16:58:40.319 YJGCDDemo[3480:125910] 显示图片---<NSThread: 0x7ff65ad00dd0>{number = 1, name = main}
     2016-08-24 16:58:40.335 YJGCDDemo[3480:125910] 图片合并完成----<NSThread: 0x7ff65ad00dd0>{number = 1, name = main}
     */
    // 效率不高 需要等图片1,图片2都下载完了后才合并
    // 优化 使用队列组可以让图片1 图片2的下载任务同事进行,且当两个下载任务都完成的时候回到主线程进行显示。

}
/**
 *  使用队列组解决(方式二)
 */
- (void)groupMergeImage{
    //步骤
    //  1、创建一个队列组
    //  2、开启一个任务下载图片1
    //  3、开启一个任务下载图片2
    //  同时执行下载图片1  和 下载图片2操作
    //  4、等group中的所有任务都执行完毕,再回到主线程执行其他操作

    // 1、创建一个队列租
    dispatch_group_t group = dispatch_group_create();

    // 2、开启一个任务下载图片1
    __block UIImage *image1 = nil;
    dispatch_group_async(group, global_queue, ^{
        image1 = [self requestImageData:@"http://h.hiphotos.baidu.com/image/pic/item/dc54564e9258d109a4d1165ad558ccbf6c814d23.jpg"];
        NSLog(@"图片1下载完成--- %@", [NSThread currentThread]);
    });

    // 3、开启一个任务下载图片2
    __block UIImage *image2 = nil;
    dispatch_group_async(group, global_queue, ^{
        image2 = [self requestImageData:@"http://5.26923.com/download/pic/000/335/06efd7b7d40328f1470d4fd99a214243.jpg"];
        NSLog(@"图片2下载完成--- %@", [NSThread currentThread]);
    });

    // 同时执行下载图片1\下载图片2操作

    // 4、等group重的所有任务都执行完毕,再回到主线程执行其他操作
    dispatch_group_notify(group, main_queue, ^{
        NSLog(@"显示图 --- %@", [NSThread currentThread]);
        self.imageViewOne.image = image1;
        self.imageViewTwo.image = image2;

        // 合并两张图片
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0.0);
        [image1 drawAsPatternInRect:CGRectMake(0, 0, 100, 200)];
        [image2 drawAsPatternInRect:CGRectMake(100, 0, 100, 200)];
        self.imageViewThree.image = UIGraphicsGetImageFromCurrentImageContext();
        // 关闭上下文
        UIGraphicsEndImageContext();

        NSLog(@"图片合并完成 --- %@", [NSThread currentThread]);
    });

    // 同时开启两个线程 分别下载图片 会比上面的效率高一点
    /**
     2016-08-24 16:58:03.785 YJGCDDemo[3467:125346] 图片1下载完成--- <NSThread: 0x7f8d13cc61c0>{number = 3, name = (null)}
     2016-08-24 16:58:03.978 YJGCDDemo[3467:125349] 图片2下载完成--- <NSThread: 0x7f8d13ece620>{number = 4, name = (null)}
     2016-08-24 16:58:03.978 YJGCDDemo[3467:125303] 显示图 --- <NSThread: 0x7f8d13e052b0>{number = 1, name = main}
     2016-08-24 16:58:03.995 YJGCDDemo[3467:125303] 图片合并完成 --- <NSThread: 0x7f8d13e052b0>{number = 1, name = main}

     */

}

八、线程间通信

// 从子线程回到主线程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     // 执行耗时的异步操作
     dispatch_async(dispatch_get_main_queue(), ^{
          //回到主线程,执行UI刷新操作
     });
 });

九、Operation和GCD的对比

优点: 不需要关心线程管理,数据同步的问题;

区别:

1、性能:GCD更接近底层,而NSOperation则更高级抽象,所以GCD在追求性能的底层操作来说,是速度最快的。

2、从异步操作之间的事务性,顺序行,依赖关系。GCD需要自己写更多的代码来实现,而NSOperationQueue已经内建了这些支持

3、如果异步操作的国学更多的被交互和UI呈现出来,NSOperationQueue会是一个更好的选择。底层代码中,任务之间不太互相依赖,而需要更高的并发能力,GCD则更有优势

十、总结

学了两天,对gcd有一些的了解,能在项目中使用多线程,不过这边也要避免很多死锁的问题,后期有时间再整理出来。四天左右把iOS多线程的几种方法都整理了下,写了demo。也算对自己的一个小小总结。

总结两点:

1、线程运行方式

dispatch_async 异步执行

dispatch_sync 同步执行

dispatch_delay 延迟执行

2、处理任务对象

dispatch_get_main_queue 主线程队列(UI线程队列)

dispatch_get_global_queue 并发线程队列

串行队列

demo地址: http://download.csdn.net/detail/yj229201093/9611939

参考链接:http://www.cnblogs.com/wendingding/p/3806821.html

感谢各路大神的博客,学无止境…

iOS多线程编程(四)------ GCD(Grand Central Dispatch)

时间: 2024-10-12 08:22:09

iOS多线程编程(四)------ GCD(Grand Central Dispatch)的相关文章

IOS学习之十七:Grand Central Dispatch(GCD)编程基础

IOS学习之十七:Grand Central Dispatch(GCD)编程基础 有过编程经验的人,基本都会接触到多线程这块. 在java中以及Android开发中,大量的后台运行,异步消息队列,基本都是运用了多线程来实现. 同样在,在ios移动开发和Android基本是很类似的一种模型. 但是很多时候,在应用开发中,我们会发现本身并没有自己编码去处理一些并发的事件,去开辟新的子线程等等. (虽然一般的调用sdk发起一个网络请求,系统都是会默认给你新起一个线程去处理的). 整个程序看上去基本就是

GCD (Grand Central Dispatch) 笔记

GCD (Grand Central Dispatch) 是Apple公司开发的一种技术,它旨在优化多核环境中的并发操作并取代传统多线程的编程模式. 在Mac OS X 10.6和IOS 4.0之后开始支持GCD. 使用GCD的一个理由就是方便.回想一下以前的多线程编程,我们会把异步调用的代码放到另外的一个函数中,并通过NSThread开启新线程来启动这段代码. 这种跳来跳去的流程对于复杂的逻辑简直就是一场灾难.更糟糕的是,调用线程时的环境对异步代码是不可见的,如果我们需要当时的临时变量的话只有

iOS开发-多线程之GCD(Grand Central Dispatch)

Grand Central Dispatch(GCD)是一个强有力的方式取执行多线程任务,不管你在回调的时候是异步或者同步的,可以优化应用程序支持多核心处理器和其他的对称多处理系统的系统.开发使用的过程中只需要将执行的任务并添加到到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务.Dispatch Queue更简单而且在实现符合需求的多线程任务时更有效率.Dispatch  Queue一般来说有三种方式,如下图: Serial执行的时候的先进先出,Concurrent

GCD Grand central Dispatch

上一章我们讨论了iOS中的网络部分的进程线程多线程的相关知识,并且初步了解了NSThread的操作.但是NSThread是有问题的.比如在某个实例,我们需要在当tableView 中显示许多的cell,而cell上是来自网络加载的图片数据.那么我们需要在代理方法中调用cell的时候对imageView添加图片.传统的UIImageView的setImage方法是在主线程中执行的.图片较少的情况下是看不出什么卡顿的效果.但是如果图片的数量很多的情况下如果不使用cell复用,xib下显示100张图片

iOS学习篇之Grand Central Dispatch(GCD)

我这里做一个简单的记录,担心现在看完了如果不做记录将来还得重头看,太浪费时间了. 全文翻译可谓是漏斗百出,将就着理解一下吧. 一些比较通俗易懂的文章推荐: GCD (Grand Central Dispatch) 笔记.Grand Central Dispatch(GCD).block && Grand Central Dispatch) 原文地址:Grand Central Dispatch (GCD) Reference CGD包含语法特征(直译是语法特征/功能),运行时库和系统增强即

iOS Grand Central Dispatch(GCD) 的简单理解

引言: GCD的全称是Grand Central Dispatch,是苹果在iOS4.0发布的一套处理并发运算方面的API.其用途是为了提高处理器多核运算的能力. GCD有点像NSOperationQueue,它们都允许程序将任务切分为多个单一任务然后提交至工作队列来并发地或者串行地执行,但GCD比之NSOpertionQueue更底层更高效. GCD的工作原理: GCD的工作原理是让程序平行排队的特定任务,根据可用的处理资源,安排他们在任何可用的处理器核心上执行任务. 一个任务可以是一个函数(

iOS多线程编程Part 3/3 - GCD

前两部分介绍了NSThread.NSRunLoop和NSOperation,本文聊聊2011年WWDC时推出的神器GCD.GCD: Grand Central Dispatch,是一组用于实现并发编程的C接口.GCD是基于Objective-C的Block特性开发的,基本业务逻辑和NSOperation很像,都是将工作添加到一个队列,由系统来负责线程的生成和调度.由于是直接使用Block,因此比NSOperation子类使用起来更方便,大大降低了多线程开发的门槛.另外,GCD是开源的喔:libd

Grand Central Dispatch(GCD)

一.Grand Central Dispatch(GCD)概要 1.什么是GCD Grand Central Dispatch(GCD)是异步执行任务的技术之一. GCD用我们难以置信的非常简洁的记述方法,实现了极为复杂繁琐的多线程编程. 例如: dispatch_async(queue, ^{ //长时间处理 //例如AR用动画识别 //例如数据库访问 //长时间处理结束,主线程使用该处理结果 dispatch_async( dispatch_get_main_queue(), ^{ //只在

【转】iOS多线程编程技术之NSThread、Cocoa NSOperation、GCD

转自容芳志的博客 简介 iOS有三种多线程编程的技术,分别是: (一)NSThread (二)Cocoa NSOperation (三)GCD(全称:Grand Central Dispatch) 这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的. 三种方式的优缺点介绍: 1)NSThread: 优点:NSThread 比其他两个轻量级 缺点:需要自己管理线程的生命周期,线程同步.线程同步对数据的加锁会有一定的系统开销 NSThread实现的技术

iOS多线程编程技术之NSThread、Cocoa NSOperation、GCD

简介iOS有三种多线程编程的技术,分别是:(一)NSThread(二)Cocoa NSOperation(三)GCD(全称:Grand Central Dispatch) 这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的. 三种方式的优缺点介绍:1)NSThread:优点:NSThread 比其他两个轻量级缺点:需要自己管理线程的生命周期,线程同步.线程同步对数据的加锁会有一定的系统开销 NSThread实现的技术有下面三种:一般使用cocoa