GCD

1.GCD

GCD(Grand Central Dispatch) 伟大的中枢调度器
GCD是苹果为多核的并行运算提出的方案

GCD会充分利用CPU内核,会自动管理自己的生命周期

GCD 函数分为同步和异步

同步函数为 dispatch_sync
异步函数为 dispatch_async
1.1 核心概念

GCD 分为任务,队列

1.1.1.定制任务

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

1.2 队列类型

1.并发和串行

1.1 并发是指多个任务同时进行(自动开启多个线程执行)

1.2 并发功能只能在异步函数中进行

2.串行队列

让任务一个接一个来执行(上个任务完成后才接着进行下一个)

2.容易混淆的术语
同步和异步 : 主要区别是,是否具有开线程的能力

并发和串行 : 这两个是队列,是执行任务的方式

3 .GCD常用的几种方法

3.1 异步并行(这种方式可以开启线程,同时执行多个任务)

#pragma mark - 异步并行队列
-(void)asyncConcurrent{

    // 创建队列
    // 第一个参数是标识,第二个是并行队列
//    dispatch_queue_t queue = dispatch_queue_create("cnw", DISPATCH_QUEUE_CONCURRENT);
    
    // 创建全局队列,第一个参数是队列的优先级,这个是获得队列,就已经存在
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 封装任务,把任务放在队列中执行
    dispatch_async(queue, ^{
        NSLog(@"1-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-------当前队列%@",[NSThread currentThread]);
    });

}

3.2 异步串行(可以开启线程,不过是开启一条线程,任务有序执行)

#pragma mark - 异步串行队列
-(void)asyncSerial
{
    // 创建队列
    dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_SERIAL);
    
    // 封装任务
    dispatch_async(queue, ^{
        NSLog(@"1-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-------当前队列%@",[NSThread currentThread]);
    });

}

3.3 同步串行(这种方法不会开启线程,任务有序执行)

#pragma mark - 同步串行队列
-(void)syncSerial
{
    // 创建队列
    dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_SERIAL);
    
    // 封装任务
    dispatch_sync(queue, ^{
        NSLog(@"1-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-------当前队列%@",[NSThread currentThread]);
    });
    
}

3.4 同步并行(不会开启线程,任务有序执行)

#pragma mark - 同步并行队列
-(void)syncConcurrent
{
    // 创建队列
    dispatch_queue_t queue = dispatch_queue_create("cnw0", DISPATCH_QUEUE_CONCURRENT);
    
    // 封装任务
    dispatch_sync(queue, ^{
        NSLog(@"1-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-------当前队列%@",[NSThread currentThread]);
    });
    
}

4.主队列的特殊性

当前队列为主队列时,异步执行不会创建线程.
有主队列时,执行方法是在主线程中执行的,不会在子线程中执行

#pragma mark - 异步主队列
-(void)asyncMain
{
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 封装任务,把任务放在队列中执行
    dispatch_async(queue, ^{
        NSLog(@"1-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-------当前队列%@",[NSThread currentThread]);
    });
    
}

打印结果

2017-03-24 15:02:48.962 GCD[10228:591925] 1-------当前队列<NSThread: 0x60800006e100>{number = 1, name = main}
2017-03-24 15:02:48.962 GCD[10228:591925] 2-------当前队列<NSThread: 0x60800006e100>{number = 1, name = main}
2017-03-24 15:02:48.962 GCD[10228:591925] 3-------当前队列<NSThread: 0x60800006e100>{number = 1, name = main}

当在同步函数中执行主队列时会出现死锁

#pragma mark - 同步主队列
-(void)syncMain
{
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    
    NSLog(@"开始执行");
    // 封装任务,把任务放在队列中执行
    dispatch_sync(queue, ^{
        NSLog(@"1-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-------当前队列%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-------当前队列%@",[NSThread currentThread]);
    });
    NSLog(@"完成执行");
}

原因是当执行主队列的任务时,会在主线程执行,而当前是同步函数,任务是一个完成后接着下一个,所以当进行第一个线程的时候,会在主线程执行,而此时主线程在等这个任务完成.

主队列特点:如果主队列发现当前主线程有任务在执行,那么主队列会暂停调用队列中的任务,知道主线程空闲为止.

如果在子线程中执行同步函数则可以有序执行任务
最后附上一张表

Snip.png
  1. GCD线程间的通信
    还是下载图片,当下载完图片以后,回到主线程,只需要是主队列就会在主线程执行

这时候同步回到主线程是不会出现死锁的,因为这一步操作是在子线程中操作的

#pragma mark - GCD线程间通信
-(void)threadMes
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"]]];
        
        // 加载好数据以后,在UI界面显示回到主线程
        //        dispatch_sync(dispatch_get_main_queue()
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
            NSLog(@"当前线程是00%@",[NSThread currentThread]);
        });
        
        NSLog(@"当前线程是01%@",[NSThread currentThread]);
    });
    

}

6.GCD的两个函数

6.1 延时操作

#pragma mark - GCD延时操作
-(void)delay
{
    NSLog(@"开始任务");
    // 第一种方法
//    [self performSelector:@selector(run) withObject:self afterDelay:2.0];
    // 第二种方法
//    [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 第三种方法GCD
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
        // 可以在主线程,也可以现在子线程
         NSLog(@"%@",[NSThread currentThread]);
        
    });

}
-(void)run
{
    NSLog(@"%@",[NSThread currentThread]);
}

6.2 只会创建一次(创建单例时会用到)

#pragma mark - GCD只会创建一次的操作
-(void)once
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"打印了几次");
    });

}

7.栅栏函数
根据表面意思就知道,栅栏:拦截的意思,就是等上边的任务执行完成以后,才可以往下进行

但栅栏函数的使用队列,不能是全局队列,不然是没效果的

#pragma mark - 栅栏函数
-(void)railings
{
     dispatch_queue_t queue = dispatch_queue_create("cnw01", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"thread1--------%@",[NSThread currentThread]);
    });
    
   
    
    dispatch_async(queue, ^{
        NSLog(@"thread3--------%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        
        NSLog(@"thread2🐛--------%@",[NSThread currentThread]);
    });   
}

8 . GCD的快速迭代(剪切文件)

#pragma mark - 快速迭代
-(void)apply
{
    // 首先获得需要剪切的文件夹
    NSString * frome = @"/Users/chenningwei/Desktop/屏幕截图";
    // 剪切到的文件夹
    NSString * to = @"/Users/chenningwei/Desktop/目标文件";
    // 得到需要剪切文件夹内的元素
    NSArray * files =  [[NSFileManager defaultManager] subpathsAtPath:frome];
    
    // 遍历数组中的元素
    NSInteger count = files.count;
    dispatch_apply(count, dispatch_get_global_queue(0, 0), ^(size_t index) {
        
        // 获得剪切文件内文件的全路径
        NSString * fromePath = [frome stringByAppendingPathComponent:files[index]];
        // 剪切到文件夹文件全路径
        NSString * toPath = [to stringByAppendingPathComponent:files[index]];
        // 剪切
        [[NSFileManager defaultManager] moveItemAtPath:fromePath toPath:toPath error:nil];
        NSLog(@"%@----%@-----%@",fromePath,toPath,[NSThread currentThread]);
    });
    

}

9 . 队列组
队列组执行是可以等上边操作完成以后,来进行下一步的操作,这是一般的异步操作实现不了了(目前我的水平)
比如:下载两张图片后,进行合成
只有两张图片下载完成以后才可以进行合并,而普通的异步操作是无序的实现不了,就需要用到队列组来实现,下面看代码

#pragma mark - 队列组下载图片
-(void)queues
{
    // 创建队列组
    dispatch_group_t group = dispatch_group_create();
    // 全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    // 下载第一张图片
    dispatch_group_async(group, queue, ^{
        NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"];
        NSData * data = [NSData dataWithContentsOfURL:url];
        self.image1= [UIImage imageWithData:data];
        
    });
    dispatch_group_async(group, queue, ^{
        NSURL * url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1490352377321&di=57b814f647287d559e264311887c5e9c&imgtype=0&src=http%3A%2F%2Fimg.sj33.cn%2Fuploads%2Fallimg%2F201402%2F102J0KJ-24.jpg"];
        NSData * data = [NSData dataWithContentsOfURL:url];
        self.image2= [UIImage imageWithData:data];
        
    });
    
    // 等两张图片下载完以后进行合成
    dispatch_group_notify(group, queue, ^{
        
        // 开始图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        
        //画图
        [self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
        self.image1 = nil;
        [self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
        self.image2 = nil;
        // 根据图形上下文得到一张图片
        UIImage * image = UIGraphicsGetImageFromCurrentImageContext();
        // 关闭图形上下文
        UIGraphicsEndPDFContext();
        
        // 设置图片
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView1.image = image;
        });
        
        
    });

    
}

10 . ARC环境下的单例模式

 .1 首先定义一个全局变量
    static Tool * _instance;
 .2 重写allocWithZone方法
+(instancetype)allocWithZone:(struct _NSZone *)zone
在这方法里创建单例
 
// 第一种懒加载方法
// 防止多条线程共享一块资源出现的安全问题,加互斥锁
@synchronized(self){
   if ( _instance == nil ){
      _instance = [super  allocWithZone:zone];
      }
}
   return  _instance;
// 第二种利用GCD创建
 static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    
    return _instance;
// 在.h文件中可以定义个类方法,供外界调用,在.m文件中的实现方法
+(instancetype)shareTool
{
    return [[self alloc] init];
}

// 但是为了安全起见,会重写
-(id)copyWithZone:(NSZone *)zone
{
    return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
    return _instance;
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容