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-------当前队列{number = 1, name = main}
2017-03-24 15:02:48.962 GCD[10228:591925] 2-------当前队列{number = 1, name = main}
2017-03-24 15:02:48.962 GCD[10228:591925] 3-------当前队列{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(@"完成执行");
}

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

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

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

GCD_第1张图片
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;
}

你可能感兴趣的:(GCD)