美文网首页
Objective-C 线程篇(三): GCD的实现

Objective-C 线程篇(三): GCD的实现

作者: Tenloy | 来源:发表于2018-11-10 15:27 被阅读122次

    GCD (Grand Central Dispatch),在系统级即iOS和OS X的核心XNU内核级上实现,所以开发者无论如何努力编写线程关系代码,性能都不可能胜过XNU内核级所实现的GCD。

    Dispatch Queue实现

    # Dispatch Quene实现的所需

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

    # 用于实现Dispatch Queue的几个软件组件框架

    • 组件libdispatch提供Dispatch Quene技术。
    • 组件Libc(pthreads)提供pthread_workquene技术。
    • 组件XNU内核提供workquene技术。

    # 执行上下文

    Dispatch Quene通过结构体和链表,被实现为FIFO队列。

    Block并不是直接加入FIFO队列,而是先加入Dispatch Continuation这一dispatch_continuation_t类型结构体中,然后再加入FIFO队列。该Dispatch Continuation用于记忆Block所属的Dispatch Group和其他一些信息,相当于一般常说的执行上下文

    上一篇在将Global Dispatch Queue的时候,我们介绍过8种类型,这8种Global Dispatch Quene各使用一个pthread_workquene。

    GCD初始化时,使用pthread_workquene_creat_np函数生成"pthread_workquene"
    pthread_workquene包含在Libc提供的pthreads API 中。
    其使用bsdthread_register和workq_open系统调用,"在初始化XNU内核的workquene之后获取workquene信息"
    
    XNU内核持有4种workquene:
    WORKQUENE_HIGH_PRIOQUENE
    WORKQUENE_Default_PRIOQUENE
    WORKQUENE_Low_PRIOQUENE
    WORKQUENE_BG_PRIOQUENE
    
    优先级与Global Dispatch Quene的四种执行优先级相同。
    

    Global Dispatch Queue --> Libc pthread_wordqueue --> XNU workqueue

    网图 侵删

    # Dispatch Queue执行Block的过程

      1. 在Global Dispatch Queue 中执行Block时,libdispatch 从Global Dispatch Queue自身的FIFO队列取出Dispatch Continuation
      1. 调用pthread_workqueue_additem_np函数将该Global Dispatch Queue 本身、符合其优先级的workqueue信息以及执行Dispatch Continuation的回调函数等传递给参数。
      1. pthread_workqueue_additem_np函数使用workq_kernreturn系统调用,通知workqueue增加应当执行的项目。
        根据该通知,XNU内核基于系统状态判断是否要生成线程。如果是Overcommit优先级的Global Dispatch Queue ,workqueue则始终生成线程(该线程虽然与iOS和OS X中通常使用的线程大致相同,但是有一部分pthread API不能使用)。
        因为workqueue生成的线程在实现用于workqueue的线程计划表中运行,他的上下文切换(shift context)与普通的线程有很大的不同。这也是我们使用GCD的原因。
      1. workqueue的线程 --> 执行pthread_workqueue函数 --> 该函数调用libdispatch的回调函数。在该回调函数中执行加入到Global Dispatch Queue中的下一个Block。
      1. Block执行结束后,进行通知Dispatch Group结束、释放Dispatch Continuation等处理,开始准备执行加入到Global 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_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, ^{
            // 处理结束,取消Dispatch Source
            dispatch_source_cancel(source);
        });
    
        /* 指定取消Dispatch Source时的处理 */
        dispatch_source_set_cancel_handler(source, ^{
            /* 释放Dispatch Source(自身) */
            dispatch_release(source);
        });
    
        /* 启动Dispatch Source */
        dispatch_resume(source);
    

    与上面代码非常相似的代码,使用在了Core Foundation框架的用于异步网络的API CFSocket中。因为Foundation框架的异步网络API是通过CFSocket实现的,所以可享受到仅使用Foundation框架的Dispatch Source(即GCD)带来的好处。

    一旦将任务追加到Dispatch Queue中,就没有办法将任务取消,也没有办法在执行中取消任务。Dispatch Source是可以取消的,而且取消时的处理可以block的形式作为参数配置。在必须使用kqueue的情况下,推荐大家使用Dispatch Source,比较简单

    相关文章

      网友评论

          本文标题:Objective-C 线程篇(三): GCD的实现

          本文链接:https://www.haomeiwen.com/subject/ijgsxqtx.html