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. AutoCad2008 部分快捷键

    编组开关:  Ctrl+Shift+A 查看扩展属性命令: xdlist 加载新的菜单栏命令:menu

  2. [z]查表空间使用情况

    SELECT UPPER(F.TABLESPACE_NAME) "表空间名",D.TOT_GROOTTE_MB "表空间大小(M)",D.TOT_GROOTTE ...

  3. xml的解析与创建——bing到youdao导入文件的转换

    首先是为了解决一个问题:如何将必应单词本中记录的单词转入到有道词典中去.实际上,必应词典可以导出xml文件,但是该文件有道词典无法解析.这里涉及到xml的解析和创建了. 代码如下: import ja ...

  4. [修改后]html+css 做成一个可浏览的表格

    现在表格内容需要显示的要求如下: 1, 表格很大,界面放不小,需要放到div中. 2, 在div中可以用scroll滑动查看. 3, td中的内容保持在一行中. 4, 可以点击tr,然后可以选中并了解 ...

  5. 搭建高可用mongodb集群(三)—— 深入副本集内部机制

    在上一篇文章<搭建高可用mongodb集群(二)—— 副本集> 介绍了副本集的配置,这篇文章深入研究一下副本集的内部机制.还是带着副本集的问题来看吧! 副本集故障转移,主节点是如何选举的? ...

  6. QQ屠龙转世-挖矿

    ※◆☆★☆◆※欢迎使用QQ屠龙转世辅助,如有疑问请联系作者QQ:82850696*2*测试版已停用*1*2014-8-27 14:05:59*哈密*E2873D0137C6D04F42E088AA46 ...

  7. 时间“Thu Aug 14 2014 14:28:06 GMT+0800”的转换

    var date = "Thu Aug 14 2014 14:28:06 GMT+0800"; var va = DateTime.ParseExact(date, "d ...

  8. vi

    e! 放弃所有修改,从上次保存文件开始再编辑 shift+g 最后一行 gg 第一行 u 恢复上一次操作 如果查找下一个,按"n"即可. set nu 显示行号 编辑模式下111g ...

  9. 使用 KGDB 调试 Kernel On Red Hat Linux

    1. KGDB 简介         KGDB  提供了一种使用 GDB 调试 Linux 内核的机制.使用 KGDB 可以象调试普通的应用程序那样,在内核中进行设置断点.检查变量值.单步跟踪程序运行 ...

  10. 手机GPS为什么能在室内定位?

      为什么手机在室内也能定位?大部分人知道手机会通过GPS进行定位,其实手机定位系统并不是和我们的RTK完全一样的,因为那样就无法解释为何在室内也能定位了,这里我来科普一下智能手机的那些定位方法.   ...