本文是根据文顶顶老师的博客学习总结而来,如有不妥之处,还望指出。http://www.cnblogs.com/wendingding/p/3807716.html

概览:

/*
纯c语言,提供了非常多强大的函数
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的cpu内核
GCD会自动管理线程的生命周期(创建线程,调度任务。销毁线程)
程序员只需告诉GCD要执行的任务,不需要编写任何线程管理代码 GCD核心概念:1)任务(执行什么操作) 2)队列(存放任务)
GCD会自动从队列中取出任务,放到对应的线程中执行
任务的取出遵循FIFO原则:先进先出,后进后出 常用函数:
同步函数:dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block)
1)不具备开线程的能力,不能开线程
2)任务执行的方式:同步 异步函数:dispatch_async(dispatch_queue_t queue, dispatch_block_t block)
1)具备开线程的能力,可以开线程
2)任务执行的方式:异步 队列类型:
并发队列:
1)可以让多个任务并发(同时)执行
2)并发功能只有在异步函数(dispatch_async)下才有效
串行队列:
1)让任务一个接一个的执行 同步、异步:能不能开启新的线程
并发、串行:任务的执行方式

一. 任务和队列

GCD中有2个核心概念

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

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

GCD的使用就2个步骤

(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个术语比较容易混淆:同步、异步、并发、串行

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

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

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

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

并发:多个任务并发(同时)执行

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

3. 串行队列

GCD中获得串行有2种途径

(1)使用dispatch_queue_create函数创建串行队列:

dispatch_queue_t  dispatch_queue_create(const char *label,  dispatch_queue_attr_t attr); // 队列名称, 队列属性,一般用NULL即可

//label:队列名称

//attr:队列属性,为NULL时,默认为串行队列

DISPATCH_QUEUE_CONCURRENT:并发队列

DISPATCH_QUEUE_SERIAL:串行队列

示例:

dispatch_queue_t queue = dispatch_queue_create("wendingding", NULL); // 创建一个串行队列

dispatch_queue_t queue = dispatch_queue_create("wendingding", DISPATCH_QUEUE_CONCURRENT); // 创建一个并发队列

dispatch_queue_t queue = dispatch_queue_create("wendingding", DISPATCH_QUEUE_SERIAL); // 创建一个串行队列

dispatch_release(queue); // 非ARC需要释放手动创建的队列

(2)使用主队列(跟主线程相关联的队列)

主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行

使用dispatch_get_main_queue()获得主队列

示例:

dispatch_queue_t queue = dispatch_get_main_queue();

4. 并发队列

GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建

使用dispatch_get_global_queue函数获得全局的并发队列

dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); // 此参数暂时无用,用0即可

dispatch_queue_t queue = dispatch_queue_create("wendingding", DISPATCH_QUEUE_CONCURRENT); // 创建一个并发队列

示例:

这个参数是留给以后用的,暂时用不上,传个0。

第一个参数为优先级,这里选择默认的。获取一个全局的默认优先级的并发队列。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 获得全局并发队列

常见的创建方式:

//TODO: 异步函数 + 并发队列:
//开辟新的子线程,任务并发执行。开辟线程的个数不是由任务的个数决定,而是由GCD自己决定
-(void)asyncConcurrent{ // 1)创建队列 //全局并发队列
// dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); /*
第一个参数:队列的名字
第二个参数:队列的类型
DISPATCH_QUEUE_CONCURRENT:并发队列
DISPATCH_QUEUE_SERIAL:串行队列
*/
dispatch_queue_t queue = dispatch_queue_create("com.binge.www", DISPATCH_QUEUE_CONCURRENT);
//2)封装任务,把任务添加到队列
dispatch_async(queue, ^{
//需要执行的任务
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{ NSLog(@"2----%@",[NSThread currentThread]);
});
} //TODO:异步函数 + 串行队列:
//只会开辟一条子线程,所有任务在该子线程中串行执行。
-(void)asyncSerial{ // 1)创建队列
/*
第一个参数:队列的名字
第二个参数:队列的类型
DISPATCH_QUEUE_CONCURRENT:并发队列
DISPATCH_QUEUE_SERIAL:串行队列
*/
dispatch_queue_t queue = dispatch_queue_create("com.binge.www", DISPATCH_QUEUE_SERIAL);
//2)封装任务,把任务添加到队列
dispatch_async(queue, ^{ //需要执行的任务
NSLog(@"1----%@",[NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"2----%@",[NSThread currentThread]);
});
} //TODO:同步函数 + 并发队列:
//不会开辟新的子线程,所有任务在当前线程中串行执行。
-(void)syncConcurrent{ // 1)创建队列
/*
第一个参数:队列的名字
第二个参数:队列的类型
DISPATCH_QUEUE_CONCURRENT:并发队列
DISPATCH_QUEUE_SERIAL:串行队列
*/
dispatch_queue_t queue = dispatch_queue_create("com.binge.www", DISPATCH_QUEUE_CONCURRENT);
//2)封装任务,把任务添加到队列
dispatch_sync(queue, ^{
//需要执行的任务
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{ NSLog(@"2----%@",[NSThread currentThread]);
});
} //TODO:同步函数 + 串行队列:
//不会开辟新的子线程,所有任务在当前线程中串行执行。
-(void)syncSerial{ // 1)创建队列
/*
第一个参数:队列的名字
第二个参数:队列的类型
DISPATCH_QUEUE_CONCURRENT:并发队列
DISPATCH_QUEUE_SERIAL:串行队列
*/
dispatch_queue_t queue = dispatch_queue_create("com.binge.www", DISPATCH_QUEUE_SERIAL);
//2)封装任务,把任务添加到队列
dispatch_sync(queue, ^{
//需要执行的任务
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{ NSLog(@"2----%@",[NSThread currentThread]);
});
} //TODO:异步函数 + 主队列:
//不会开启线程,所有任务在主线程中串行执行
-(void)asyncMain{ // 1)创建主队列
dispatch_queue_t queue = dispatch_get_main_queue(); //2)封装任务,把任务添加到队列
dispatch_async(queue, ^{
//需要执行的任务
NSLog(@"1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{ NSLog(@"2----%@",[NSThread currentThread]);
});
} //TODO:同步函数 + 主队列:
//死锁
-(void)syncMain{ // 1)创建主队列
dispatch_queue_t queue = dispatch_get_main_queue(); //2)封装任务,把任务添加到队列
dispatch_sync(queue, ^{
//需要执行的任务
NSLog(@"1----%@",[NSThread currentThread]);
}); }

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

同步函数

(1)并发队列:不会开线程

(2)串行队列:不会开线程

异步函数

(1)并发队列:能开启N条线程

(2)串行队列:开启1条线程

常见用法:

1.从子线程回到主线程

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{

       //在这执行一些耗时操作

        dispatch_async(dispatch_get_main_queue(), ^{

            //回到主线程进行操作

        });

    });

2.延迟调用

(1)

[self performSelector:@selector(run) withObject:nil afterDelay:5.0];

注意:如果将

[self performSelector:@selector(run) withObject:nil afterDelay:5.0];放到子线程中,例如:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[self performSelector:@selector(run) withObject:nil afterDelay:5.0];

});

这时候程序不会运行 run 方法。

正确写法:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[self performSelector:@selector(run) withObject:nil afterDelay:5.0];

[[NSRunLoop currentRunLoop] run];

});

(2)

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

//5秒后执行这里的操作,可以选择在哪个线程中执行,即:可以用dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 代替 dispatch_get_main_queue()

});

dispatch_group_t:

如果想在dispatch_queue(队列)中所有的任务执行完成后再做某种操作,在串行队列中,可以把该操作放到最后一个任务执行完成后继续,但是在并行队列中怎么做呢。这就有dispatch_group 成组操作

    dispatch_group_t groupT = dispatch_group_create();
dispatch_queue_t queueT = dispatch_queue_create("gcd.www", DISPATCH_QUEUE_CONCURRENT);//创建一个并发队列 dispatch_group_async(groupT, queueT, ^{
sleep();
NSLog(@"这是第一个任务=%@",[NSThread currentThread]);
});
dispatch_group_async(groupT, queueT, ^{
NSLog(@"这是第二个任务=%@",[NSThread currentThread]);
});
dispatch_group_async(groupT, queueT, ^{ NSLog(@"这是第三个任务=%@",[NSThread currentThread]);
});
dispatch_group_notify(groupT, queueT, ^{
NSLog(@"上面3个任务完成来到这里=%@",[NSThread currentThread]);
});

我们来看一下打印结果:

-- ::23.185829+ NSThread-GCD-NSOperation[:] 这是第二个任务=<NSThread: 0x1c42799c0>{number = , name = (null)}
-- ::23.186054+ NSThread-GCD-NSOperation[:] 这是第三个任务=<NSThread: 0x1c42799c0>{number = , name = (null)}
-- ::28.190651+ NSThread-GCD-NSOperation[:] 这是第一个任务=<NSThread: 0x1c02788c0>{number = , name = (null)}
-- ::28.191438+ NSThread-GCD-NSOperation[:] 上面3个任务完成来到这里=<NSThread: 0x1c02788c0>{number = , name = (null)}

我们可以看到,只有当3个任务都完成的时候(即时第一个任务延时了5秒),才会调用dispatch_group_notify。

思考:如果在队列中添加了子线程,并有延时操作,上面的方法还适用吗?我们再来看代码演示。

dispatch_group_t groupT = dispatch_group_create();
dispatch_queue_t queueT = dispatch_queue_create("gcd.www", DISPATCH_QUEUE_CONCURRENT);//创建一个并发队列 dispatch_group_async(groupT, queueT, ^{
dispatch_async(queueT, ^{
sleep();
NSLog(@"这是第一个任务=%@",[NSThread currentThread]);
}); });
dispatch_group_async(groupT, queueT, ^{
NSLog(@"这是第二个任务=%@",[NSThread currentThread]);
});
dispatch_group_async(groupT, queueT, ^{ NSLog(@"这是第三个任务=%@",[NSThread currentThread]);
});
dispatch_group_notify(groupT, queueT, ^{
NSLog(@"上面3个任务完成来到这里=%@",[NSThread currentThread]);
});

再来看打印结果:

-- ::10.060225+ NSThread-GCD-NSOperation[:] 这是第二个任务=<NSThread: 0x1c0460200>{number = , name = (null)}
-- ::10.060466+ NSThread-GCD-NSOperation[:] 这是第三个任务=<NSThread: 0x1c0460200>{number = , name = (null)}
-- ::10.060701+ NSThread-GCD-NSOperation[:] 上面3个任务完成来到这里=<NSThread: 0x1c04692c0>{number = , name = (null)}
-- ::15.065466+ NSThread-GCD-NSOperation[:] 这是第一个任务=<NSThread: 0x1c0460200>{number = , name = (null)}

我们发现:当第一个任务是在子线程中做了延时操作时,队列中任务还没全部完成,就调用了dispatch_group_notify。

这时,我们需要用到:

dispatch_group_enter   //group内任务数+1

dispatch_group_leave   //group内任务数-1

当group内任务数为0时,任务全部完成,触发dispatch_group_notify

大致就这么理解吧。

继续看代码:

 dispatch_group_enter(groupT);//  +1
dispatch_group_async(groupT, queueT, ^{
dispatch_async(queueT, ^{
sleep();
NSLog(@"currentThread1=%@",[NSThread currentThread]);
dispatch_group_leave(groupT);// 任务完成-1
});
}); dispatch_group_enter(groupT);// +1
dispatch_group_async(groupT, queueT, ^{
dispatch_async(queueT, ^{
sleep();
NSLog(@"currentThread2=%@",[NSThread currentThread]);
dispatch_group_leave(groupT);// 任务完成-1
}); }); dispatch_group_enter(groupT);// +1
dispatch_group_async(groupT, queueT, ^{
dispatch_async(queueT, ^{
NSLog(@"currentThread3=%@",[NSThread currentThread]);
dispatch_group_leave(groupT);// 任务完成-1
}); }); dispatch_group_notify(groupT, dispatch_get_main_queue(), ^{
NSLog(@"currentThread4=%@",[NSThread currentThread]);
});

看打印结果:

-- ::07.862952+ NSThread-GCD-NSOperation[:] currentThread3=<NSThread: 0x1c4278e40>{number = , name = (null)}
-- ::09.865219+ NSThread-GCD-NSOperation[:] currentThread2=<NSThread: 0x1c0663f40>{number = , name = (null)}
-- ::12.867091+ NSThread-GCD-NSOperation[:] currentThread1=<NSThread: 0x1c0079880>{number = , name = (null)}
-- ::12.867881+ NSThread-GCD-NSOperation[:] currentThread4=<NSThread: 0x1c0079680>{number = , name = main}

可以看到,解决了上面的问题,只有当group内所有任务都完成的时候才会出发dispatch_group_notify。

再介绍一个知识点:

再思考一下:如果有多个线程异步执行,正常情况下,这些线程是同时执行的,这时有个需求,要求先执行前面两个,后面的再执行,怎么办?

看代码:

dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"");
}); dispatch_async(queue, ^{
NSLog(@"");
}); dispatch_barrier_async(queue, ^{
//先执行此函数前面的代码
}); dispatch_async(queue, ^{
NSLog(@"");
});
dispatch_async(queue, ^{
NSLog(@"");
});

dispatch_barrier_async

栅栏函数     先执行栅栏函数前面,再执行栅栏函数后面的

注意:不能使用全局并发队列 dispatch_get_global_queue

iOS 开发多线程 —— GCD(1)的更多相关文章

  1. ios开发多线程--GCD

    引言 虽然GCD使用很广,而且在面试时也经常问与GCD相关的问题,但是我相信深入理解关于GCD知识的人肯定不多,大部分都是人云亦云,只是使用过GCD完成一些很简单的功能.当然,使用GCD完成一些简单的 ...

  2. IOS开发 多线程GCD

    Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main queue,通过dispat ...

  3. iOS 开发多线程篇—GCD的常见用法

    iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...

  4. iOS开发多线程篇—GCD介绍

    iOS开发多线程篇—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 G ...

  5. iOS开发多线程篇—GCD的基本使用

    iOS开发多线程篇—GCD的基本使用 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进 ...

  6. iOS开发多线程篇—GCD的常见用法

    iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...

  7. iOS开发多线程篇—GCD简介

    iOS开发多线程篇—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 G ...

  8. iOS开发多线程篇 08 —GCD的常见用法

    iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...

  9. iOS开发多线程篇 07 —GCD的基本使用

    iOS开发多线程篇—GCD的基本使用 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进 ...

随机推荐

  1. 课程一(Neural Networks and Deep Learning),第四周(Deep Neural Networks)—— 0.学习目标

    Understand the key computations underlying deep learning, use them to build and train deep neural ne ...

  2. sparkshell运行sql报错: java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

    下载msyql的连接driver https://download.csdn.net/download/xz360717118/10662304 把其中一个: mysql-connector-java ...

  3. Postman入门

  4. 使用binlog2sql针对mysql进行数据恢复

    MySQL闪回原理与实战 DBA或开发人员,有时会误删或者误更新数据,如果是线上环境并且影响较大,就需要能快速回滚.传统恢复方法是利用备份重搭实例,再应用去除错误sql后的binlog来恢复数据.此法 ...

  5. JavaScript -- Window-Resize

    -----036-Window-Resize.html----- <!DOCTYPE html> <html> <head> <meta http-equiv ...

  6. Silverlight中使用MVVM(4)—演练

    本来打算用MVVM实现CRUD操作的,这方面例子网上资源还挺多的,毕竟CRUD算是基本功了,因为最近已经开始学习Cailburn框架了,感觉时间 挺紧的,这篇就实现其中的更新操作吧.         ...

  7. 系统启动时队列自动下单--ServletContextListener

    package com.liying.pear.queue; import javax.servlet.ServletContextEvent; import javax.servlet.Servle ...

  8. PPT定时器小记

    在日常会议汇报中,往往会出现超时的情况.此时需要一种优雅提醒讲演者加快速度的方式.PPT定时器就是其中的一种方法. ppttimer,一款在GitHub上发布的开源软件,基本能满足我在本项工作中的需求 ...

  9. .20-浅析webpack源码之compile流程-Template模块

    这里的编译前指的是开始触发主要的事件流this-compilaiton.compilation之前,由于还有一些准备代码,这一节全部弄出来. 模块基本上只走构造函数,具体的方法调用的时候再具体讲解. ...

  10. 记一次SQL注入实战

    刚发现漏洞时,我就已经成功实现了注入,因为怕发到网上后被玩坏,一直没有发布.今天去看了看,原网页已经无法访问了,现在发出来应该就没有什么大问题了. 本文仅供学习交流,目的是为了构建更加安全的网络环境! ...