GCD使用详解

1.什么是GCD
全称是Grand Central Dispatch,可译为“牛X的中枢调度器”
纯C语言,提供了非常多强大的函数

2.GCD的优势
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核、四核)
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

3.GCD中有2个核心概念
任务:执行什么操作
队列:用来存放任务
将任务添加到队列中
GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循队列的FIFO原则:先进先出,后进后出

有4个术语比较容易混淆:同步、异步、并发、串行
同步和异步决定了要不要开启新的线程
同步:在当前线程中执行任务,不具备开启新线程的能力
异步:在新的线程中执行任务,具备开启新线程的能力

并发和串行决定了任务的执行方式
并发:多个任务并发(同时)执行
串行:一个任务执行完毕后,再执行下一个任务

代码分析上面的四个概念:
#pragma mark - 1异步并发
- (void)asyncGlobal {
    // 取得全局并发队列
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 使用异步调用
    dispatch_async(queue, ^{
        NSLog(@"result1, %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"result2, %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"result3, %@",[NSThread currentThread]);
    });
    
}

#pragma mark - 2异步串行
- (void)asyncSerial {
    // 创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("Serial", NULL);
    
    // 使用异步调用
    dispatch_async(queue, ^{
        NSLog(@"result1, %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"result2, %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"result3, %@",[NSThread currentThread]);
    });
    
}

#pragma mark - 3同步并发
- (void)syncGlobal {
    // 取得全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 使用同步调用
    dispatch_sync(queue, ^{
        NSLog(@"result1, %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"result2, %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"result3, %@",[NSThread currentThread]);
    });
    
}

#pragma mark - 4同步串行
- (void)syncSerial {
    // 创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("Serial", NULL);
    
    // 使用同步调用
    dispatch_sync(queue, ^{
        NSLog(@"result1, %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"result2, %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"result3, %@",[NSThread currentThread]);
    });
    
}

上面的代码,我们可以通过观察打印线程信息 [NSThread currentThread] 来判断是否有开启新的线程及开启新的线程的情况。上面的执行结果,可以用下面的图表来描述:
GCD使用详解_第1张图片

用文字总结,结果如下:

同步 (sync)  不开起新的线程

            并发队列:     不开起新的线程(用默认的主线程)

            串行队列:     不开起新的线程(用默认的主线程)

 

异步 (async) 开启新的线程

            并发队列:     能开启多条线程(取出队列中的任务,子线程执行;接着取出队列中后面的任务,开辟其他线程执行这条任务....)

            串行队列:     只开启一条子线程(取出队列中的任务,子线程执行,执行完毕后,继续取出任务,子线程继续执行....)



4.线程间通信示例

从子线程回到主线程

dispatch_async(

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

   //执行耗时的异步操作...

     dispatch_async(dispatch_get_main_queue(),^{

        // 回到主线程,执行UI刷新操作

        });

});


上面的代码片段是经典的线程间通信使用方法。

Example:

从网络下载图片资源,并显示到主线程UI上面

// 并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 开启子线程
    dispatch_async(queue, ^{
        NSURL *url = [NSURL URLWithString:@"http://pic.cnr.cn/list/201209/W020120914588236390160.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];

        
        // 讲image信息在主线程上面呈现(这样就不会卡死了)
        dispatch_async(dispatch_get_main_queue(), ^{
            self.myImageView.image = image;
        });
    });

5. 队列组

有这么一种需求
首先:分别异步执行两个耗时的操作
其次:等个异步操作都执行完毕后,再回到主线程执行操作
dispatch_group_t group =  dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // 执行1个耗时的异步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    // 等前面的异步操作都执行完毕后,回到主线程...
});

Example:
从网络异步下载两张图片,当两张图片均下载完毕后,完成图片的拼接操作
效果图如下:

实现代码如下:
#pragma mark - 并发下载多张图片
- (void)downloadImage {
    dispatch_group_t group = dispatch_group_create();
    
    __block UIImage *image1 = nil;
    dispatch_group_async(group, GlobalQueue, ^{
        image1 = [self imageWithUrl:@"http://www.2qqtouxiang.cn/uploads/allimg/100917/1_100917230241_2.jpg"];
    });
    
    __block UIImage *image2 = nil;
    dispatch_group_async(group, GlobalQueue, ^{
        image2 = [self imageWithUrl:@"http://img3.douban.com/view/photo/albumicon/public/p1446733286.jpg"];
    });
    
    // 上面两个GCD是异步同时下载图片的,下面这句代码是等到两张图片全部下载完毕后便会执行
    dispatch_group_notify(group, MainQueue, ^{
        self.image1.image = image1;
        self.image2.image = image2;
        
        // 拼接图片
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(100, 100), NO, 0.0);
        [image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        [image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        self.myImage.image = UIGraphicsGetImageFromCurrentImageContext();
        
        UIGraphicsEndImageContext();
    });

}

- (UIImage *)imageWithUrl:(NSString *)urlStr {
    NSURL *url = [NSURL URLWithString:urlStr];
    NSData *data = [NSData dataWithContentsOfURL:url];
    return [UIImage imageWithData:data];
}

6. GCD其他好用的方法
#pragma mark - 延时执行
- (void)delay {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延时3秒执行~~~");
    });
}

#pragma mark - 不管调用多少次只执行一次
- (void)doOnce {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"一次性执行");
    });
}



你可能感兴趣的:(多线程,gcd,并行编程)