整理自一篇gcd简书

一、一个异步、同步、并行、串行的表格

(第一个使用Markdown)
多核是gcd的基础

表格总结.png

二、任务:同步、异步、栅栏

同步:dispatch_sync
异步:dispatch_async
栅栏:dispatch_barrier_async
同步和异步我就不举🌰了
栅栏(zha lan)函数(我也不懂,查的百度)
和名字相同,栅栏的作用,等barrier之前的函数执行结束再执行barrier函数,之后的函数必须在barrier函数执行完毕之后才能运行,上代码吧
有栅栏函数

    dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"第一个异步任务%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第二个异步任务%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"栅栏函数%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第三个异步任务%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第四个异步任务%@",[NSThread currentThread]);
    });

运行结果

2017-05-05 13:22:46.847 retainCount[16150:1140225] 第二个异步任务<NSThread: 0x6000000688c0>{number = 5, name = (null)}
2017-05-05 13:22:46.847 retainCount[16150:1140226] 第一个异步任务<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.848 retainCount[16150:1140226] 栅栏函数<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.849 retainCount[16150:1140226] 第三个异步任务<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.849 retainCount[16150:1140225] 第四个异步任务<NSThread: 0x6000000688c0>{number = 5, name = (null)}

一二任务被栅栏函数分割在上方,三四任务被分割在下方
有个规律,栅栏任务会在上一个任务的同一个线程开始,栅栏任务的下一个任务也会和栅栏任务在同一个线程开始(本人不知道原因)

注释掉栅栏函数之后运行结果

2017-05-05 13:24:26.448 retainCount[16183:1142163] 第四个异步任务<NSThread: 0x600000260980>{number = 6, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142160] 第三个异步任务<NSThread: 0x600000260740>{number = 5, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142180] 第一个异步任务<NSThread: 0x600000260940>{number = 3, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142161] 第二个异步任务<NSThread: 0x608000265ec0>{number = 4, name = (null)}

异步任务时间同时开始,在四个新开的线程中
【栅栏函数整理自http://www.cnblogs.com/denz/p/5277666.html

三、串行队列和并行队列

队列不想说太多了,之前有一篇已经说过一些了,再说可能会乱。但是看到一个总结感觉很好,写在这里

  • 同步异步,影响是否开启新的线程
  • 串行并行,影响任务的是否同时执行还是按顺序执行

四、GCD的使用

之前有写,但是感觉这个大牛写的更好,把代码又都写了一遍。

- (void)createUI
{
    UIButton *myBtn = [[UIButton alloc]initWithFrame:CGRectMake(20, 20, 300,50)];
    myBtn.backgroundColor = [UIColor redColor];
    [myBtn setTitle:@"子线程、同步任务、主队列" forState:UIControlStateNormal];
    
    [myBtn addTarget:self action:@selector(ChildSyncMain) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn];
    
    UIButton *myBtn1 = [[UIButton alloc]initWithFrame:CGRectMake(20, 90, 300,50)];
    myBtn1.backgroundColor = [UIColor redColor];
    [myBtn1 setTitle:@"主线程、同步任务、主队列" forState:UIControlStateNormal];
    [myBtn1 addTarget:self action:@selector(MainSyncMain) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn1];
    
    UIButton *myBtn2 = [[UIButton alloc]initWithFrame:CGRectMake(20, 160, 300,50)];
    myBtn2.backgroundColor = [UIColor redColor];
    [myBtn2 setTitle:@"主线程、异步任务、主队列" forState:UIControlStateNormal];
    
    [myBtn2 addTarget:self action:@selector(MainAsyncMain) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn2];
    
    UIButton *myBtn3 = [[UIButton alloc]initWithFrame:CGRectMake(20, 230, 300,50)];
    myBtn3.backgroundColor = [UIColor redColor];
    [myBtn3 setTitle:@"同步任务、并发队列" forState:UIControlStateNormal];
    
    [myBtn3 addTarget:self action:@selector(SyncConcurrent) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn3];
    
    UIButton *myBtn4 = [[UIButton alloc]initWithFrame:CGRectMake(20, 300, 300,50)];
    myBtn4.backgroundColor = [UIColor redColor];
    [myBtn4 setTitle:@"同步任务、串行队列" forState:UIControlStateNormal];
    
    [myBtn4 addTarget:self action:@selector(SyncSerial) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn4];
    
    UIButton *myBtn5 = [[UIButton alloc]initWithFrame:CGRectMake(20, 370, 300,50)];
    myBtn5.backgroundColor = [UIColor redColor];
    [myBtn5 setTitle:@"异步任务、串行队列" forState:UIControlStateNormal];
    
    [myBtn5 addTarget:self action:@selector(AsyncSerial) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn5];
    
    UIButton *myBtn6 = [[UIButton alloc]initWithFrame:CGRectMake(20, 440, 300,50)];
    myBtn6.backgroundColor = [UIColor redColor];
    [myBtn6 setTitle:@"异步任务、并行队列" forState:UIControlStateNormal];
    
    [myBtn6 addTarget:self action:@selector(AsyncConcurrent) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn6];
}

- (void)ChildSyncMain
{
    //第一个参数是优先级,第二个参数是保留字
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //开启一个子线程
    dispatch_async(queue, ^{
        //获取主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        //同步任务
        dispatch_sync(queue, ^{
            NSLog(@"任务所在线程%@",[NSThread currentThread]);
        });
    });
}

- (void)MainSyncMain
{
    //主队列,此时在主线程
    dispatch_queue_t queue = dispatch_get_main_queue();
    //同步任务
    dispatch_sync(queue, ^{
        NSLog(@"任务所在线程%@",[NSThread currentThread]);
    });
}

- (void)MainAsyncMain
{
    //与上一函数区别在于异步
    dispatch_queue_t queue = dispatch_get_main_queue();
    //异步任务
    dispatch_async(queue, ^{
        NSLog(@"任务所在线程%@",[NSThread currentThread]);
    });
}

- (void)SyncConcurrent
{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //三个同步任务,放在并发队列,查看所在线程
    dispatch_sync(queue, ^{
        NSLog(@"1------所在线程%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2------所在线程%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3------所在线程%@",[NSThread currentThread]);
    });
}

- (void)SyncSerial
{
    dispatch_queue_t queue = dispatch_queue_create("firstSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"1------所在线程%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2------所在线程%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3------所在线程%@",[NSThread currentThread]);
    });
}

- (void)AsyncSerial
{
    
    dispatch_queue_t queue = dispatch_queue_create("SecondSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"1------所在线程%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2------所在线程%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3------所在线程%@",[NSThread currentThread]);
    });
}
//这个大牛写的太好了,不好意思抄了,直接copy过来吧。参数什么的解释的太好了
- (void)AsyncConcurrent
{
    /*
     执行任务
     dispatch_async
     dispatch_sync
     */
    
    /*
     第一个参数: 队列的名称
     第二个参数: 告诉系统需要创建一个并发队列还是串行队列
     DISPATCH_QUEUE_SERIAL :串行
     DISPATCH_QUEUE_CONCURRENT 并发
     */
    //    dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
    
    // 系统内部已经给我们提供好了一个现成的并发队列
    /*
     第一个参数: iOS8以前是优先级, iOS8以后是服务质量
     iOS8以前
     *  - DISPATCH_QUEUE_PRIORITY_HIGH          高优先级 2
     *  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      默认的优先级 0
     *  - DISPATCH_QUEUE_PRIORITY_LOW:          低优先级 -2
     *  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:
     
     iOS8以后
     *  - QOS_CLASS_USER_INTERACTIVE  0x21 用户交互(用户迫切想执行任务)
     *  - QOS_CLASS_USER_INITIATED    0x19 用户需要
     *  - QOS_CLASS_DEFAULT           0x15 默认
     *  - QOS_CLASS_UTILITY           0x11 工具(低优先级, 苹果推荐将耗时操作放到这种类型的队列中)
     *  - QOS_CLASS_BACKGROUND        0x09 后台
     *  - QOS_CLASS_UNSPECIFIED       0x00 没有设置
     
     第二个参数: 废物
     */
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    /*
     第一个参数: 用于存放任务的队列
     第二个参数: 任务(block)
     
     GCD从队列中取出任务, 遵循FIFO原则 , 先进先出
     输出的结果和苹果所说的原则不符合的原因: CPU可能会先调度其它的线程
     
     能够创建新线程的原因:
     我们是使用"异步"函数调用
     能够创建多个子线程的原因:
     我们的队列是并发队列
     */
    dispatch_async(queue, ^{
        NSLog(@"任务1  == %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2  == %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3  == %@", [NSThread currentThread]);
    });
}

这是个带按钮的操作,效果主要看打印,恩界面是这样的

gcd操作界面.png

打印是这样的(第二个按钮会造成死锁因此就没有打印)

2017-05-05 15:34:33.983 retainCount[17357:1229839] 任务所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:39.538 retainCount[17357:1229839] 任务所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.420 retainCount[17357:1229839] 1------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.421 retainCount[17357:1229839] 2------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.422 retainCount[17357:1229839] 3------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.729 retainCount[17357:1229839] 1------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.730 retainCount[17357:1229839] 2------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.731 retainCount[17357:1229839] 3------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:43.561 retainCount[17357:1232087] 1------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:43.561 retainCount[17357:1232087] 2------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:43.562 retainCount[17357:1232087] 3------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232087] 任务1  == <NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232083] 任务3  == <NSThread: 0x600000276e80>{number = 5, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232112] 任务2  == <NSThread: 0x60800026bf40>{number = 4, name = (null)}

具体的分析统一写在这里吧

首先还是得记住前面所写的

  • 同步异步决定是否开启线程
  • 并行串行决定是否顺序执行任务
  • 主队列中有主线程,在其中的任务不会创建新的线程
    记住这三条就可以了
    好了,开始分析
  1. 子线程、同步任务、主队列
    看这个解释之前先看一下第二中情况,这一次我们在子线程中开启了同步任务,然后block放在主队列,我们的同步任务只要等到block执行就可以了,不会死锁。

  2. 主线程、同步任务、主队列
    关于主线程和主队列的概念,我看了好几个博客,还是不太懂,不过有一个原则就是主队列中的任务要在主线程中执行,有了这个原则可能就好解释了。我们在主线程中调用了同步函数,调用的同步函数的操作就在block中。但是block被我们放在了主队列中,主队列是串行队列,block在这个队列的最后。前面的所有任务如果无法完成就不会调用到block。但是前面任务完成的条件是同步函数要完成。于是就死锁了。

  3. 主线程、异步任务、主队列
    唯一和以上不同的是,异步任务的优先级低,不会抢夺系统资源,会在cpu空闲的时候调用,因此不会造成死锁。

  4. 同步任务、并发队列
    同步任务不会创建新的线程,因此会在主线程中调用

  5. 同步任务、串行队列
    同上

  6. 异步任务、串行队列
    异步任务会开启新的线程,因为串行队列,只开启一个新的线程

  7. 异步任务、并行队列
    异步任务会开启新的线程,因为并行队列,会开启多个线程

五、线程通信

举个图片下载的🌰

- (void)threadCommunication
{
    //创建一个队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //异步任务
    dispatch_async(queue, ^{
        //下载图片
        NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1493985090619&di=43c16e2e96b2bd4f2cb694ffd7ad0c0c&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fforum%2Fw%3D580%2Fsign%3Daa1a5e38afec8a13141a57e8c7029157%2F0092bc389b504fc22854cad2e3dde71191ef6d1e.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        //主线程更新ui
        dispatch_sync(dispatch_get_main_queue(), ^{
            self.ThreadImageView.image = image;
        });
        NSLog(@"图片下载结束");
    });
}

运行结果

线程通信.png

五、队列组

使用场景:等俩那个异步操作执行结束后执行某项操作,栅栏函数也可以实现
关键函数

dispatch_group_t//创建队列组
dispatch_group_async//队列其中一个异步任务
dispatch_ground_notify//所有异步操作结束后的操作

示例代码(附带图片绘制和缓存沙盒)

    dispatch_queue_t queue = dispatch_queue_create("yfq", DISPATCH_QUEUE_CONCURRENT);
    __block UIImage *image1 = nil;
    __block UIImage *image2 = nil;
//block内部可以使用block外部的变量,如果需要改变block外部变量,需要添加__block修饰符
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494220438978&di=5fb8127873860c26a59e9ef775030528&imgtype=0&src=http%3A%2F%2Fd.5857.com%2Fjryh_170321%2Fdesk_001.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        image1 = [UIImage imageWithData:data];
        NSLog(@"图片1下载完成");
    });
    dispatch_group_async(group, queue, ^{
        NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494220164621&di=8bd30fe6a3bde6463dc5dd5c0684009a&imgtype=0&src=http%3A%2F%2Fimage.tianjimedia.com%2FuploadImages%2F2015%2F164%2F22%2FX3UBUK29Z641.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        image2 = [UIImage imageWithData:data];
        NSLog(@"图片2下载完成");
    });
    
    dispatch_group_notify(group, queue, ^{
        UIGraphicsBeginImageContext(CGSizeMake(200, 100));
        [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
        [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        dispatch_async(dispatch_get_main_queue(), ^{
            self.GrounpImageView.image = newImage;
            NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"123.png"];
            [UIImagePNGRepresentation(newImage) writeToFile:path atomically:YES];
        });
    });

沙盒中的截图

sandboxImage.png

通过NSHomeDirectory()打印直接进入模拟器的沙盒,可以看到图片保存了。
图片绘制照搬大体分为几个步骤吧

  1. UIGraphicsBeginImageContext(cg)//绘制区域
  2. [image drawInRect:cg];//添加内容
  3. UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();//获取绘制内容的图片
  4. UIGraphicsEndImageContext();//关闭图形上下文

绘制图片这块好像有点复杂,一句两句说不清,有空再开一篇说吧。
Markdown自带的表格代码不好用(可能是我没用对),使用没效果,不过自动保存很好用,再也不用担心不小心关闭浏览器了。
【转载并整理于http://www.jianshu.com/p/bc45569adee2

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

推荐阅读更多精彩内容