GCD Tips

GCD

Grand Central Dispatch(GCD)是Apple开发的一个多核编程的解决方法。该方法在MacOSX10.6雪豹中首次推出,并随后被引入到了iOS4.0中。GCD是一个替代诸如NSThread, NSOperationQueue等技术的很高效和强大的技术。GCD可以帮助咱们使用很是简洁高效的方法实现复杂繁琐的多线程编程。html

下面的例子列举了简单的异步线程处理,在后台处理完耗时的程序以后,在主线程更新UI。编程

dispatch_async(queue, ^{
       /*
        * ...
        * 长时间处理的Code
        */
        
        //处理结束,主线程更新UI
        // 使用dispatch_get_main_queue() 得到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            // ... code
            // 执行须要在主线程执行的更新UI代码
        });
    });
复制代码

GCD的实现原理

简单来讲,GCD的实现须要使用这些工具:bash

  • 用于管理追加的Block的C语言实现的FIFO队列
  • Atomic函数中实现的额用于排他控制的轻量级信号
  • 用于管理线程的C语言层实现的一些容器

一般,应用程序中编写的线程管理应用的代码要在系统iOS和OS X的核心XNU内核级上实现。所以,不管编程人员如何努力编写管理线程的代码,在性能方面也不可能赛过XNU内核级所实现的GCD。网络

使用GCD要比使用pthreads和NSThread这些通常的多线程编程API更好。而且,若是使用GCD就没必要编写为操做线程反复出现的相似的代码(这被称为固定源代码片断),而能够在线程中集中实现处理内容。咱们尽可能多使用GCD或者使用了Cocoa框架GCD的NSOperationQueue类等API。多线程

用于实现Dispatch Queue而使用的软件组件。app

组件名称 提供技术
libdispatch Dispatch Queue
Libc(pthreads) pthread_workqueue
XNU内核 workqueue

编程人员所使用GCD的API所有包含在libdispatch库中的C语言函数。Dispatch Queue经过结构体和链表,被实现为FIFO队列。FIFO队列管理是经过dispatch_async函数所追加的Block。框架

Block并非直接加入FIFO队列,而是先加入Dispatch Continuation这一==dispatch_continuation_t==类型结构体中,而后再加入FIFO队列。该Dispatch Continuation用于记忆Block所属的Dispatch Group和其余一些信息,至关于通常常说的执行上下文。异步

Dispatch Queue可经过==dispatch_set_target_queue==函数设定,能够设定执行该Dispatch Queue处理的Dispatch Queue为目标。该目标可像串珠子同样,设定多个链接在一块儿的Dispatch Queue。可是在链接串的最后必须设定为Main Dispatch Queue,或各类优先级的Global Dispatch Queue,或是准备用于Serial Dispatch Queue的各类优先级的Global Dispatch Queue。async

Main Dispatch Queue在RunLoop 中执行Block。ide

Global Dispatch Queue有以下8中:

Global Dispatch Queue(High Priority)
Global Dispatch Queue(Default Priority)
Global Dispatch Queue(Low Priority)
Global Dispatch Queue(Background Priority)
Global Dispatch Queue(High Overcommit Priority)
Global Dispatch Queue(Default Overcommit Priority)
Global Dispatch Queue(Low Overcommit Priority)
Global Dispatch Queue(Background Overcommit Priority)
复制代码

优先级中附有Overcommit的Global Dispatch Queue使用在Serial Dispatch Queue中。如Overcommit 这个名称所示,无论系统状态如何,都会强制生成线程的Dispatch Queue。 这8种Global Dispatch Queue各使用1个pthread_workqueue。GCD初始化时,使用pthread_workqueue_create_np函数生成pthread_workqueue。

pthread_workqueue包含在Libc提供的pthreads API中。其使用bsdthread_register和workq_open系统调用,在初始化XNU内核的workqueue以后获取workqueue信息。

XNU内核持有4中workqueue:

WORKQUEUE_HIGH_PRIORITY
WORKQUEUE_DEFAULT_PRIORITY
WORKQUEUE_LOW_PRIORITY
WORKQUEUE_BG_PRIORITY
复制代码

以上为4中执行优先级的workqueue。该执行优先级与Global Dispatch Queue的4种执行优先级相同。

下面看一下Dispatch Queue中执行Block的过程。当在Global Dispatch Queue中执行Block时,libdispatch从Global Dispatch Queue自身的FIFO队列中提出Dispatch Continuation,调用pthread_workqueue_additem_np函数。将该Global Dispatch Queue自身、符合其优先级的workqueue信息以及为执行Dispatch Continuation的回调函数等传递给参数。

该线程虽然与iOS和OS X中一般使用的线程大体相同,可是有一部分pthread API不能使用。详细信息科参考苹果的官方文档《并列编程指南》的“与POSIX线程的互换性”一节。

另外,由于workqueue生成的线程在实现用于workqueue的线程计划表中运行,因此与通常线程的上下文切换不一样。这里也隐藏着使用GCD的缘由。

Block执行结束后,进行通知Dispatch Group结束、释放Dispatch Continuation等处理,开始准备执行加入到Globar Dispatch Queue的下一个Block。


Dispatch Source

GCD中除了主要的Dispatch Queue外,还有不太引人注目的Dispatch Source。它是BSD系内核惯有功能kqueue的包装。

kqueue是XNU内核中发生各类事件时,在应用程序编程方执行处理的技术。其CPU负荷很是小,尽可能不占用资源。kqueue能够说是应用程序处理XNU内核中发生的各类事件的方法中最优秀的一种。

Dispatch Source可处理如下事件:

DISPATCH_SOURCE_TYPE_DATA_ADD	变量增长
DISPATCH_SOURCE_TYPE_DATA_OR	变量OR
DISPATCH_SOURCE_TYPE_MACH_SEND	MACH端口发送
DISPATCH_SOURCE_TYPE_MACH_RECV	MACH端口接收
DISPATCH_SOURCE_TYPE_PROC	检测到与进程相关的事件
DISPATCH_SOURCE_TYPE_READ	可读取文件映像
DISPATCH_SOURCE_TYPE_SIGNAL	接收信号
DISPATCH_SOURCE_TYPE_TIMER	定时器
DISPATCH_SOURCE_TYPE_VNODE	文件系统有变动
DISPATCH_SOURCE_TYPE_WRITE	可写入文件映像
复制代码

事件发生时,在指定的Dispatch Queue中可执行事件的处理。 下面咱们使用DISPATCH_SOURCE_TYPE_READ,异步读取文件映像。

*
    __block size_t total = 0;
    size_t size = 1024 * 10;//要读取的字节数
    char * buff = (char *)malloc(size);

    /*
     *  设定为异步映像
     */
    fcntl(sockfd, F_SETFL, O_NONBLOCK);

    /*
     *  获取用于追加事件处理的Global Dispatch Queue
     */    
    dispatch_queue_t queue = dispatc_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    /*
     *  基于READ事件做成Dispatch Source
     */  
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0 , queuq);

    /*
     *  指定发生READ事件时执行的处理
     */    
    dispatch_source_set_event_handler(source, ^{
          /*
           *  获取可读取的字节数
           */
          size_t available = dispatch_source_get_data(source);
          /*
           *  从映像中读取
           */
          int length = read(sockfd, buff, available);
          /*
           *  发生错误时取消Dispatch Source
           */
          if (length < 0)
          {
                // 错误处理
                dispatch_source_cancel(source);
          }

          total += length;

          if (total == size)
          {
                // buff 的处理

                // 处理结束,取消Dispatch Source
                dispatch_source_cancel(source);
          }
    });

    /*
     *  指定取消Dispatch Source时的处理
     */
    dispatch_source_set_cancel_handler(source, ^{
        free(buff);
        close(sockfd);

        /*
         *  释放Dispatch Source(自身)
         */
        dispatch_release(source);
    });

    /*
     *  启动Dispatch Source
     */

    dispatch_resume(source);
复制代码

与上面代码很是类似的代码,使用在了Core Foundation框架的用于异步网络的API CFSocket中。由于Foundation框架的异步网络API是经过CFSocket实现的,因此可享受到仅使用Foundation框架的Dispatch Source(即GCD)带来的好处。

参考文档

相关文章
相关标签/搜索
本站公众号
   欢迎关注本站公众号,获取更多信息