GCD笔记
http://www.cocoachina.com/applenews/devnews/2013/1210/7506_2.html
1. 全称Grand Central Dispatch
2. 特性
支持同步或异步任务处理,
串行或并行的处理队列(Dispatch Queue),
非系统调用的信号量机制,
定时任务处理,
进程、文件或网络的监听任务

3. Dispatch Queue的特性类型
Dispatch Queue是一个任务执行队列,可以让你异步或同步地执行多个Block或函数。Dispatch Queue是FIFO的,即先入队的任务总会先执行。目前有三种类型的Dispath Queue:

3.1.串行队列(Serial dispatch queue)

3.2.并发队列(Concurrent dispatch queue)

3.3.主队列(Main dispatch queue)

4. 创建串行队列

串行队列一次只能处理一个任务,可以由用户调用dispatch_queue_create创建:

  1. dispatch_queue_t queue;
  2. queue = dispatch_queue_create("com.example.MyQueue", NULL);

dispatch_queue_create第一个参数是串行队列标识,一般用反转域名的格式表示以防冲突;第二个参数是queue的类型,设为 NULL时默认是DISPATCH_QUEUE_SERIAL,将创建串行队列,在必要情况下,你可以将其设置为 DISPATCH_QUEUE_CONCURRENT来创建自定义并行队列。

5. 创建并行队列

并行队列可以同时处理多个任务,在不得已的情况下可以用dispatch_queue_create创建,

但通常, 我们都要用系统预定义的并行队列, 即全局队列(Global Concurrent Dispatch Queues)。

目前系统预定义了四个不同运行优先级的全局队列,我们可以通过dispatch_get_global_queue来获取它们。

  1. dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_get_global_queue第一个参数是队列的优先级,分别对应四个全局队列:

DISPATCH_QUEUE_PRIORITY_HIGH

DISPATCH_QUEUE_PRIORITY_DEFAULT

DISPATCH_QUEUE_PRIORITY_LOW

DISPATCH_QUEUE_PRIORITY_BACKGROUND

dispatch_get_global_queue中第二个参数目前系统保留,请设置为0即可。

6. 获取主队列

主队列是一个特殊的队列,它是系统预定义的运行在主线程的一个Dispatch Queue。

可以通过dispatch_get_main_queue来获取唯一的主队列。

主队列一般运行一些需要与主线程同步的一些短时任务。

  1. dispatch_queue_t mainQueue = dispatch_get_main_queue();

7. 获取当前队列

你可以通过dispatch_get_current_queue获取运行时的队列:

  1. dispatch_queue_t currentQueue = dispatch_get_current_queue();

如果在队列执行任务中调用,返回执行此任务的队列;

如果在主线程中调用,将返回主队列;

如果在一般线程(非主线程线程非队列执行任务)中调用,返回DISPATCH_QUEUE_PRIORITY_DEFAULT全局队列。

8. 如何在队列中异步运行任务?

你可以随时向一个队列中添加一个新任务,只需要调用一下dispatch_async即可:

  1. dispatch_async(aQueue, ^{
  2. //Do some work;
  3. });

dispatch_async中的任务是异步执行的,就是说dispatch_async添加任务到执行队列后会立刻返回,而不会等待任务执行完成。

9. 如何在队列中同步运行任务?

然而,必要的话,你也可以调用dispatch_sync来同步的执行一个任务:

  1. dispatch_sync(aQueue, ^{
  2. //Do some work;
  3. });

dispatch_sync会阻塞当前线程直到提交的任务完全执行完毕。

10. Dispatch Queue如何进行内存管理?

除了系统预定义的Dispatch Queue,我们自定义的Dispatch Queue需要手动的管理它的内存。

10.1 通过引用计数来管理queue.

dispatch_retain和dispatch_release这两个函数可以控制Dispatch
Queue的引用计数(同时可以控制后面会讲到的Dispatch Group和Dispatch Source的引用计数)。

10.2当Dispatch
Queue引用计数变为0后,就会调用finalizer。

10.3 Dispatch Queue的finalizer是什么?

finalizer是Dispatch
Queue销毁前调用的函数,用来清理Dispatch
Queue的相关资源。

可以用dispatch_set_finalizer_f函数来设置Dispatch
Queue的finalizer,这个函数同时可以设置Dispatch Group和Dispatch Source的销毁函数(后面会讲到)。

  1. void dispatch_set_finalizer_f(dispatch_object_t object, dispatch_function_t finalizer);

11.Dispatch Queue的上下文环境数据

11.1 如何设置上下文环境数据?

我们可以为每个Dispatch
Queue设置一个自定义的上下文环境数据,调用dispatch_set_context来实现。同时我们也可以用
dispatch_get_context获取这个上下文环境数据,这个函数同时可以设置Dispatch Group和Dispatch
Source的上下文环境数据(后面会讲到)。

  1. void dispatch_set_context(dispatch_object_t object,void *context);
  2. void * dispatch_get_context(dispatch_object_t object);
11.2 注意Dispatch Queue并不保证这个context不会释放,不会对它进行内存管理控制。我们需要自行管理context的内存分配和释放。一般我们分配内存设置context后,可以在finalizer里释放context占有的内存。

12. 并行执行循环

在编程过程中,我们经常会用到for循环,而且for循环要做很多相关的任务。比如:

  1. for (i = 0; i < count; i++) {
  2. //do a lot of work here.
  3. doSomething(i);
  4. }

12.1 如何利用并发特性,使循环处理更高效,前提是,每次循环之间没有耦合依赖。

如果for循环中处理的任务是可并发的,显然放到一个线程中处理是很慢的,GCD提供两个函数dispatch_apply和 dispatch_apply_f,dispatch_apply是用于Block的,而dispatch_apply_f可以用于c函数,它们可以替代 可并发的for循环,来并行的运行而提高执行效率。

  1. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  2. dispatch_apply(count, queue, ^(size_t i) {
  3. //do a lot of work here.
  4. doSomething(i);
  5. });
上述代码中, count参数:是循环次数,size_t i参数是指循环的计数器。

13. Dispatch Group。多任务并发,如果存在任务间依赖性,可以把多个任务放入到一个Group里。这个group将被异步执行

有时候我们进行下一步操作,而这个操作需要等待几个任务处理完毕后才能继续,这时我们就需要用的Dispatch Group(类似thread join)。我们可以把若干个任务放到一个Dispatch Group中:

  1. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  2. dispatch_group_t group = dispatch_group_create();
  3. dispatch_group_async(group, queue, ^{
  4. // Some asynchronous work
  5. });

dispatch_group_async跟dispatch_async一样,会把任务放到queue中执行,不过它比dispatch_async多做了一步操作就是把这个任务和group相关联。

把一些任务放到Dispatch Group后,我们就可以调用dispatch_group_wait来等待这些任务完成。若任务已经全部完成或为空,则直接返回,否则等待所有任务完成后返回。注意:返回后group会清空。

  1. dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
  2. // Do some work after.
  3. dispatch_release(group);

14. Dispatch信号量

很多程序设计都设计到信号量,生产者-消费者模型在多线程编程中会频繁的使用。GCD提供了自己的一套信号量机制。

  1. dispatch_semaphore_t sema = dispatch_semaphore_create(RESOURCE_SIZE);
  2. dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
  3. //do some work here.
  4. dispatch_semaphore_signal(sema);

dispatch_semaphore_wait用来获取信号量,若信号量为0,则等待直到信号量大于0。在处理任务结束后,应释放相关资源并调用dispatch_semaphore_signal使信号量增加1个。

15. Dispatch Source

Dispatch Source是GCD中监听一些系统事件的有个Dispatch对象,它包括定时器、文件监听、进程监听、Mach port监听等类型。

可以通过dispatch_source_create创建一个Dispatch Source:

  1. dispatch_source_t dispatch_source_create(
  2. dispatch_source_type_t type,
  3. uintptr_t handle,
  4. unsigned long mask,
  5. dispatch_queue_t queue);

这里可以指定Dispatch Source的类型,type可以为文件读或写、进程监听等。handle为监听对象的句柄,如果是文件就是文件描述符,如果是进程就是进程ID。 mask用来指定一些想要监听的事件,它的意义取决于type。queue指定事件处理的任务队列。

创建好Dispatch Source后,我们要为Dispatch Source设置一个事件处理模块。可以用dispatch_source_set_event_handler或dispatch_source_set_event_handler_f来设置:

  1. void dispatch_source_set_event_handler(
  2. dispatch_source_t source,
  3. dispatch_block_t handler);

设置好Dispatch Source后就可以调用dispatch_resume来启动监听。如果相应的事件发生就会触发事件处理模块。

同时我们也可以设置一个取消处理模块:

  1. dispatch_source_set_cancel_handler(mySource, ^{
  2. close(fd); // Close a file descriptor opened earlier.
  3. });

取消处理模块会在Dispatch Source取消时调用。

下面介绍一下主要的Dispatch Source类型和示例代码。

定时器

定时器Dispatch Source可以每隔一个固定的时间处理一下任务。

  1. dispatch_source_t CreateDispatchTimer(uint64_t interval,
  2. uint64_t leeway,
  3. dispatch_queue_t queue,
  4. dispatch_block_t block)
  5. {
  6. dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
  7. 0, 0, queue);
  8. if (timer)
  9. {
  10. dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
  11. dispatch_source_set_event_handler(timer, block);
  12. dispatch_resume(timer);
  13. }
  14. return timer;
  15. }
  16. void MyCreateTimer()
  17. {
  18. dispatch_source_t aTimer = CreateDispatchTimer(30ull * NSEC_PER_SEC,
  19. 1ull * NSEC_PER_SEC,
  20. dispatch_get_main_queue(),
  21. ^{ MyPeriodicTask(); });
  22. // Store it somewhere for later use.
  23. if (aTimer)
  24. {
  25. MyStoreTimer(aTimer);
  26. }
  27. }

dispatch_after和dispatch_after_f

有时候我们只想处理一次延迟任务,可以用dispatch_after和dispatch_after_f

  1. void dispatch_after(
  2. dispatch_time_t when,
  3. dispatch_queue_t queue,
  4. dispatch_block_t block);

监听文件事件

监听文件事件分好几个类型,有读、写、属性的监听。

读取文件

  1. dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, queue);
  2. dispatch_source_set_event_handler(source, ^{
  3. // Get some data from the source variable, which is captured
  4. // from the parent context.
  5. size_t estimated = dispatch_source_get_data(source);
  6. // Continue reading the descriptor...
  7. });
  8. dispatch_resume(source);

写文件

  1. dispatch_source_t writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,
  2. fd, 0, queue);
  3. if (!writeSource)
  4. {
  5. close(fd);
  6. return NULL;
  7. }
  8. dispatch_source_set_event_handler(writeSource, ^{
  9. size_t bufferSize = MyGetDataSize();
  10. void* buffer = malloc(bufferSize);
  11. size_t actual = MyGetData(buffer, bufferSize);
  12. write(fd, buffer, actual);
  13. free(buffer);
  14. // Cancel and release the dispatch source when done.
  15. dispatch_source_cancel(writeSource);
  16. });

监听文件属性,当文件被改名时。

  1. dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,
  2. fd, DISPATCH_VNODE_RENAME, queue);
  3. if (source)
  4. {
  5. // Copy the filename for later use.
  6. int length = strlen(filename);
  7. char* newString = (char*)malloc(length + 1);
  8. newString = strcpy(newString, filename);
  9. dispatch_set_context(source, newString);
  10. // Install the event handler to process the name change
  11. dispatch_source_set_event_handler(source, ^{
  12. const char*  oldFilename = (char*)dispatch_get_context(source);
  13. MyUpdateFileName(oldFilename, fd);
  14. });
  15. // Install a cancellation handler to free the descriptor
  16. // and the stored string.
  17. dispatch_source_set_cancel_handler(source, ^{
  18. char* fileStr = (char*)dispatch_get_context(source);
  19. free(fileStr);
  20. close(fd);
  21. });
  22. // Start processing events.
  23. dispatch_resume(source);
  24. }
  25. else
  26. close(fd);

监听进程事件,当进程退出时

  1. dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC,
  2. parentPID, DISPATCH_PROC_EXIT, queue);
  3. if (source)
  4. {
  5. dispatch_source_set_event_handler(source, ^{
  6. MySetAppExitFlag();
  7. dispatch_source_cancel(source);
  8. dispatch_release(source);
  9. });
  10. dispatch_resume(source);
  11. }

监听中断信号

    1. dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, queue);
    2. if (source)
    3. {
    4. dispatch_source_set_event_handler(source, ^{
    5. MyProcessSIGHUP();
    6. });
    7. // Start processing signals
    8. dispatch_resume(source);
    9. }
 

GCD笔记的更多相关文章

  1. 二进制GCD算法解析

    UPD 2018.3.30 这个好像就是更相减损术的样子emmm UPD 2018.5.22 好像不是更相减损术而是叫Stein算法的样子emmm 蒟蒻来做个二进制GCD笔记. 为什么要写这个东西呢, ...

  2. 数论学习笔记之解线性方程 a*x + b*y = gcd(a,b)

    ~>>_<<~ 咳咳!!!今天写此笔记,以防他日老年痴呆后不会解方程了!!! Begin ! ~1~, 首先呢,就看到了一个 gcd(a,b),这是什么鬼玩意呢?什么鬼玩意并不 ...

  3. 格而知之9:一些关于GCD的笔记

    1.最近在重读当年刚开始学习多线程时的笔记,发觉其中有一些地方还是比较容易模糊,于是整理这篇笔记记录一下. 执行方式和队列 2.队列用来存放管理要执行的任务,它分为并发队列(Concurrent Di ...

  4. BZOJ 2820: YY的GCD [莫比乌斯反演]【学习笔记】

    2820: YY的GCD Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1624  Solved: 853[Submit][Status][Discu ...

  5. BZOJ 2818: Gcd [欧拉函数 质数 线性筛]【学习笔记】

    2818: Gcd Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4436  Solved: 1957[Submit][Status][Discuss ...

  6. iOS开发笔记5:多线程之NSThread、NSOperation及GCD

    这篇主要总结下iOS开发中多线程的使用,多线程开发一般使用NSThread.NSOperation及GCD三种方式,常用GCD及NSOperation. 1.NSThread 创建线程主要有以下三种方 ...

  7. iOS多线程之GCD学习笔记

    什么是GCD 1.全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 2.纯C语言,提供了非常多强大的函数 GCD的优势 GCD是苹果公司为多核的并行运算提出的解决方案 G ...

  8. 多线程-GCD学习笔记

    ********************************* 基本概念 *********************************** 1. Grand Central Dispatch ...

  9. iOS 多线程学习笔记 —— GCD

    本文复制.参考自文章:iOS多线程编程之Grand Central Dispatch(GCD)介绍和使用 ,主要为了加强个人对知识的理解和记忆,不做他用.原作者声明: 著作权声明:本文由http:// ...

随机推荐

  1. Gradle笔记系列(一)

    1.Gradle概述 Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具.它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的 ...

  2. 根据第三方库spire.pdf使用指定打印机打印pdf文件

    private void button1_Click(object sender, EventArgs e) { PdfDocument doc = new PdfDocument(); string ...

  3. 使用AS编译jni文件无法编译出arm64-v8a,x86_64和mips64平台的.so文件的解决方法

    我用的插件版本是:classpath 'com.android.tools.build:gradle-experimental:0.4.0',AS集成和使用ndk编译项目参考官方demo:https: ...

  4. 一个程序员眼中的好UI

    最近接到一个项目发来的UI设计图,我觉得她给的材料很专业,特此分享. 发的RAR压缩包里面有一个images目录,里面放的都是切片好的图片. 图片切片基本上都是靠近边线切的,边上留的空白很少,这样切的 ...

  5. css3中的字体样式

    text-overform:ellipsis省略号/clip裁剪. overform:hidden溢出隐藏文字. 但是text-overflow只是用来说明文字溢出时用什么方式显示,要实现溢出时产生省 ...

  6. [PL/SQL] 如何规避异常ORA-01403

    如果mytable表中不存在 ID = 123 的数据,那么 SELECT Flag INTO flag FROM mytable WHERE ID = 123 将抛出异常ORA-01403 SELE ...

  7. IIS报错 试图加载格式不正确 的程序集解决办法

    一般都是由于系统位数不一致导致的 方法:64位启用32应用程序兼容(推荐) 思路就是把程序池设置为对应的应用程序(即到底要不要启用32位应用程序)

  8. 在Linux上安装最新版java的JDK

    之前写过一篇关于MC建服的文章(http://www.cnblogs.com/apollospotatolikett/p/6149042.html),文章中使用的JDK不是最新的版本,当时没有细说如何 ...

  9. 8天入门wpf(转)

    8天入门wpf—— 第一天 基础概念介绍 8天入门wpf—— 第二天 xaml详解 8天入门wpf—— 第三天 样式 8天入门wpf—— 第四天 模板 8天入门wpf—— 第五天 数据绑定 8天入门w ...

  10. 电子科技大学第八届ACM趣味程序设计竞赛第四场(正式赛)题解

    A. Picking&Dancing 有一列n个石子,两人交替取石子,每次只能取连续的两个,取走后,剩下的石子仍然排成1列.问最后剩下的石子数量是奇数还是偶数. 读懂题意就没什么好说的. #i ...