学习GCD要掌握几个概念

任务:需要执行的代码块可以看作一个任务

队列:把任务放到队列里,遵循先进先出的原则

队列又分为串行队列和并行队列

  串行队列:顺序执行

  并发队列:同时执行多个任务

同步:在当前线程执行 (不开辟新线程)

异步:在另一条线程执行(会开辟新线程)

gcd是支持arc的,不用我们进行内存管理

1.串行队列,同步添加一个任务(这个操作其实没有什么意义,这里仅仅作一个示例)

- (void)serialQueueSync
{
//1.创建串行队列,注意:第一个参数为队列标识C语言前面不用带@
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL); //2.同步执行任务
//一般只要向串行队列添加同步任务,会马上执行
dispatch_sync(queue, ^{ NSLog(@"这是一个同步任务,在线程:%@中执行",[NSThread currentThread]); });
}

2.串行队列,异步添加一个任务

- (void)serialQueueAsync
{
//1.创建串行队列,注意:第一个参数为队列标识C语言前面不用带@
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL); //2.异步执行任务
dispatch_async(queue, ^{ NSLog(@"这是一个异步任务,在线程:%@中执行",[NSThread currentThread]); });
}

下面我们用一个for循环来理解串行队列和并发队列的区别

串行队列

- (void)serial
{
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
//异步执行任务
for(int i = 0; i < 5; i++)
{
dispatch_async(queue, ^{ NSLog(@"这是一个异步任务,i = %d ,在线程:%@中执行",i,[NSThread currentThread]); });
}
}

结果可以看到只开辟了一个线程,所有任务排队在这个线程里执行

并发队列

- (void)concurrent
{
//并发队列
dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
//异步执行任务
for(int i = 0; i < 5; i++)
{
dispatch_async(queue, ^{ NSLog(@"这是一个异步任务,i = %d,在线程:%@中执行",i,[NSThread currentThread]); });
}
}

结果可以看到开辟了多条线程同时执行任务

并发队列同步执行:不会开辟新线程

- (void)concurrentSync
{
//并发队列
dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
//同步执行任务
for(int i = 0; i < 5; i++)
{
dispatch_sync(queue, ^{ NSLog(@"这是一个异步任务,i = %d ,在线程:%@中执行",i,[NSThread currentThread]); });
}
}

总结:同步异步决定了要不要开启新线程,串行和并发决定任务的执行方式

  • 同步:不具备开辟线程的能力
  • 异步:能开辟新线程
  • 并发:多个任务并发(同时)执行
  • 串行:按顺序执行任务

  串行队列同步执行:不开新线程,在原来线程里面顺序执行

  串行队列异步执行:开一条新线程,在新线程里顺序执行

  并发队列异步执行:开多条线程(我们并不能控制开启多少条线程,由GCD底层帮我们完成),并发执行任务

  并发队列同步执行:不开线程,在原来线程里顺序执行

可以看出,串行队列同步执行和并发队列同步执行对多线程来说没有什么实际意义,开发中也基本不会这么去用

主队列

    1.当程序启动时,就会创建一个主线程,同时有一个主队列(iOS开发中默认UI更新全在主线程中完成)

2.主队列负责在主线程上调度任务

3.异步添加任务到主队列不会开启新线程,任务在主线程中执行

4.异步添加到主队列的任务并不一定马上执行,而是顺序等待任务执行

5.同步添加任务到主队列,这是一种十分愚蠢的做法,永远不要这么去做,下面会做说明

下面的代码能帮助我们理解

异步添加任务到主队

- (void)mainQueue
{
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
for (int i = 0; i < 5; i++)
{
NSLog(@"添加第%d个任务",i);
     //异步添加
dispatch_async(mainQueue, ^{
NSLog(@"i = %d thread = %@",i,[NSThread currentThread]);
});
}
}

打印结果可以看出任务全部添加了之后才顺序执行

同步添加任务到主队列 (不要这么做,这里仅做示例)

- (void)mainQueue
{
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
for (int i = 0; i < 5; i++)
{
NSLog(@"添加第%d个任务",i);
//同步添加
dispatch_sync(mainQueue, ^{
NSLog(@"i = %d thread = %@",i,[NSThread currentThread]);
});
}
}

从打印结果我们能发现主线程被阻塞了(在view上面添加一些UI控件能更清楚的发现这一点),出现这种情况的原因是:同步任务需要马上执行,但是主线程正在执行添加任务,此时添加任务又在等待同步任务执行完成,就造成了互相等待的局面,阻塞了主线程,造成死锁(死锁的概念可以看我的另一篇博文iOS中的锁

全局队列

全局队列和并发队列的区别(这两种队列用法相似)

1.全局队列没有标示,并发队列有

2.全局队列供所有应用程序使用

3.MRC中,并发队列需要我们进行内存管理,全局队列不需要

//获得全局队列,第一个参数是指队列的服务质量(优先级)一般使用默认0,第二个参数是保留参数也填0
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);

线程间的通信(当我们在其他线程中做完数据处理,想把这些数据显示到界面的时候,就要进行线程间通信,iOS默认更新UI的操作都在主线程中执行)

- (void)communicate
{
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(globalQueue, ^{
//在这里进行异步操作
dispatch_async(mainQueue, ^{
//回到主线程更新UI
});
});
}

延迟执行(下例本质是延迟5s把任务添加到队列)

  几个参数的含义

  dispatch_time(何时, 经过多少纳秒)

NSEC_PER_SEC :1000000000ull

  所以我们要使用3 * NSEC_PER_SEC:这样才算3秒   

- (void)delay
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"这个任务延迟5s执行");
});
}

调度组 (应用场景:多个任务全部完成了,再统一通知用户,通过轮询机制判断所有组里的任务是否完成)

- (void)group
{
//创建一个调度组
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue(); //创建多个任务
dispatch_group_async(group, globalQueue, ^{ [NSThread sleepForTimeInterval:1];
NSLog(@"任务1完成");
});
dispatch_group_async(group, globalQueue, ^{ [NSThread sleepForTimeInterval:3];
NSLog(@"任务2完成");
});
dispatch_group_async(group, globalQueue, ^{ [NSThread sleepForTimeInterval:2];
NSLog(@"任务3完成");
}); //获得组里面所有任务的完成通知
dispatch_group_notify(group, mainQueue, ^{
NSLog(@"所有任务完成");
}); }

dispatch_once(只执行一次,受线程保护,常用来设计单例)

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self once];
[self once];
} - (void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只会执行一次");
}); NSLog(@"可以执行多次");
}

多线程 (四)GCD的更多相关文章

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

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

  2. [iOS]多线程和GCD

    新博客wossoneri.com 进程和线程 进程 是指在系统中正在运行的一个应用程序. 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 比如同时打开QQ.Xcode,系统就会分别 ...

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

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

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

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

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

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

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

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

  7. java 多线程四

    java 多线程一 java 多线程二 java 多线程三 java 多线程四 一个生产者,消费者的例子: import java.util.Stack; /** * Created by root ...

  8. swift 多线程及GCD

    1.基本概念 1)进程: 进程是指在系统中正在运行的一个应用程序.每个进程之间是独立的,每个进程运行在其专用且受保护的内存空间里.某进程内的线程在其它进程不可见 2)线程: 1个进程要执行任务,必须有 ...

  9. 多线程编程 - GCD(转)

    原文:http://blog.csdn.net/q199109106q/article/details/8566300 一.简介 在iOS所有实现多线程的方案中,GCD应该是最有魅力的,因为GCD本身 ...

  10. 网络与多线程---OC中多线程方法GCD(二)

    小编在前一篇中介绍了多线程实现的五种常用方法.在接下来所介绍的这种方法是最具有魅力的,最具有诱惑的实现多线程的方案---GCD 一.什么是GCD GCD是Grand Central Dispatch的 ...

随机推荐

  1. iOS开发——根据Url 获取图片尺寸

    转自:http://www.oschina.net/code/snippet_2248391_53038 // 根据图片url获取图片尺寸 +(CGSize)getImageSizeWithURL:( ...

  2. IOS UITabelView的cell

    一.Cell的重用原理 iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万个UITableViewCell对象的话,那将会耗尽iOS设备的内存.要解决该问题,需要重用 ...

  3. dom0级事件和dom2级事件

    dom0级事件 <a href="#" id="hash" onclick="fn();fn1();"> <button ...

  4. MyBatis参数传入集合之foreach动态sql

    foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合.foreach元素的属性主要有item,index,collection,open,separator,close.ite ...

  5. 《HTML5网页开发实例详解》连载(四)HTML5中的FileSystem接口

    HTML 5除了提供用于获取文件信息的File对象外,还添加了FileSystem相关的应用接口.FileSystem对于不同的处理功能做了细致的分类,如用于文件读取和处理的FileReader和Fi ...

  6. JAVA_SE复习(basic)

    一.数据类型 1.基本数据类型 Ps:有效标识符:_.字母.$开头  之后可有数字 整型:byte 1 short 2 int 4 long 8  (字节) 取值范围:其范围是从负2 的该数据类型位数 ...

  7. DEV GridControl表格数据源为空在表格中间显示提醒字符

    private static void gv_CustomDrawEmptyForeground(object sender, DevExpress.XtraGrid.Views.Base.Custo ...

  8. 同时存在两个或多个homestead 虚拟box

    开发中发现,不同版本的homestead 里面的环境各不相同,里面的node,npm等版本都不一致,如果需要添加 不同版本的homestead同时存在可以按照以下办法处理. tips: 提供可以离线下 ...

  9. Opencv 的数据结构

    opencv的基本数据结构 结构 成员 意义 CvPoint int x,y 图像中的点 CvPoint2D32f float x,y 二维空间中的点 CvPoint3D32f float x,y,z ...

  10. 关于count(1) 和 count(*)

    Q:What is the difference between count(1) and count(*) in a sql queryeg.select count(1) from emp; an ...