GCD的队列和线程的关系-GCD源码学习笔记

什么是GCD

答案参考官方文档。

什么是队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

GCD中的队列

队列的分类

队列总的来说可以分为串行队列和并行队列两种,但是在iOS中的GCD由于主队列和全局队列的特殊性,我们需要单独讨论,在这里我们把GCD中的队列细分为四种队列:串行队列、并行队列、主队列和全局并行队列。示例:

// 串行队列
    dispatch_queue_t serial = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_SERIAL);
    //并行队列
    dispatch_queue_t conque = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_CONCURRENT);
    // 主队列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //全局并行队列
    dispatch_queue_t globQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • 串行队列串行队列中,提交到队列里的block是按循序执行的,而且是一个执行完才能执行下一个。串行队列可以通dispatch_queue_create()函数创建,第二个参数可以是DISPATCH_QUEUE_SERIAL或者NULL:
dispatch_queue_t serial = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_SERIAL);
  • 并行队列队列里中的block可以同时调用多个,不需要等到前面的block执行完。并行队列可以通dispatch_queue_create()函数创建,第二个参数可以是DISPATCH_QUEUE_CONCURRENT:
dispatch_queue_t conque = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_CONCURRENT);

并行队列的创建流程跟串行队列是一样的,不同前面已经提到过,主要是队列的类型和最大并行数。并行队列的类型为OS_dispatch_queue_concurrent,最大并行数为DISPATCH_QUEUE_WIDTH_MAX,是个宏定义,定义为:

#define DISPATCH_QUEUE_WIDTH_FULL           0x1000ull
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
#define DISPATCH_QUEUE_WIDTH_MAX  (DISPATCH_QUEUE_WIDTH_FULL - 2)

DISPATCH_QUEUE_WIDTH_FULL是16进制,0x1000ull转换成10进制为4096,所以并行队列的最大并行数DISPATCH_QUEUE_WIDTH_MAX为4094。

  • 主队列本身也是个串行队列,它的特殊性在于它是和主线程绑定的。主线程自动创建runloop,主队列里的block由runloop来调用。值得注意的一点是,主队列只在主线程执行,而主线执行的不一定是主队列的。主队列无法被修改,调用 dispatch_suspend(), dispatch_resume(), dispatch_set_context()等这些方法操作主线程是无效的。我们可以通过函数dispatch_get_main_queue获取:
 dispatch_queue_t mainQueue = dispatch_get_main_queue();
  • 全局并行队列实际上也是一个并行队列,它与普通并行队列相比不一样的地方是它是由系统创建的,并且队列里的block是根据系统管理的线程池来调度的执行的。全局并行队列在系统管理的线程池之上提供了优先级桶,系统将根据需求和负载情况决定为这个线程池分配多少线程。系统会尽力保持良好的并行水平,当现有工作线程在系统调用中阻塞时,将创建新的线程。
    全局并行队列是一个共享资源,因此它需要保证每个使用全局队列的用户提交的任务不能无限量提交任务,特别是在遇到阻塞时,避免创建大量的线程。
    可以通过函数dispatch_get_global_queue()获取;同时它也是不能被修改的,调用Calls to dispatch_suspend(), dispatch_resume(), dispatch_set_context()等对它进行操作时无效的。
    通过dispatch_get_global_queue()函数来获取。第一个参数表示优先级。这里要说明是,全局队列它不是像主队列那样只有一个,而是一组队列。不同的优先级队列是不一样的,默认优先级是DISPATCH_QUEUE_PRIORITY_DEFAULT,它的值是0,第二个参数是保留参数,正常要传0。优先级的值都是系统设定好的,目前就给我们开放了几个优先级:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
队列dispatch_queue_t的本质
  • dispatch_queue_t是怎么来的
    通过dispatch_queue_create函数可以创建一个队列dispatch_queue_t:
dispatch_queue_t queue = dispatch_queue_create("com.dongjianxiong", DISPATCH_QUEUE_SERIAL);

dispatch_queue_t是什么?怎么来的?跟踪dispatch_queue_t的声明,发现是通过宏定义声明的:

DISPATCH_DECL(dispatch_queue);
#define DISPATCH_DECL(name) OS_OBJECT_DECL_SUBCLASS(name, dispatch_object)
#define OS_OBJECT_DECL_SUBCLASS(name, super) \
        OS_OBJECT_DECL_IMPL(name, )
#define OS_OBJECT_CLASS(name) OS_##name

#if OS_OBJECT_USE_OBJC
#import 
#if __has_attribute(objc_independent_class)
#define OS_OBJC_INDEPENDENT_CLASS __attribute__((objc_independent_class))
#endif // __has_attribute(objc_independent_class)
#ifndef OS_OBJC_INDEPENDENT_CLASS
#define OS_OBJC_INDEPENDENT_CLASS

#define OS_OBJECT_DECL_IMPL(name, ...) \
        OS_OBJECT_DECL_PROTOCOL(name, __VA_ARGS__) \
        typedef NSObject \
                * OS_OBJC_INDEPENDENT_CLASS name##_t

这其中name其实就是dispatch_queue,最后一行转化一下大概就是:

typedef NSObject  *dispatch_queue_t;

其实通过编译后的C++代码也能得出相同的结果:

// @protocol OS_dispatch_queue  /* @end */
 typedef NSObject/**/ * __attribute__((objc_independent_class)) dispatch_queue_t;
// @protocol OS_dispatch_queue_global  /* @end */
 typedef NSObject/**/ * __attribute__((objc_independent_class)) dispatch_queue_global_t;
// @protocol OS_dispatch_queue_serial  /* @end */
 typedef NSObject/**/ * __attribute__((objc_independent_class)) dispatch_queue_serial_t;
// @protocol OS_dispatch_queue_main  /* @end */
 typedef NSObject/**/ * __attribute__((objc_independent_class)) dispatch_queue_main_t;
// @protocol OS_dispatch_queue_concurrent  /* @end */
 typedef NSObject/**/ * __attribute__((objc_independent_class)) dispatch_queue_concurrent_t;

这里证明了dispatch_queue_t其实就是一个OC对象,它的类是OS_dispatch_queue,dispatch_queue_t是OS_dispatch_queue的实现。
在我们不只看到dispatch_queue_t 的由来,还有它的四个子类。其实这里可以看出OS_dispatch_queue实际上只是一个抽象类,我们使用的是它的具体的四个子类,这四个子类的实现分别是dispatch_queue_serial_t(串行队列)、dispatch_queue_concurrent_t(并行队列)、dispatch_queue_main_t(主队列)和 dispatch_queue_global_t(全局并行队列)。

  • 队列的继承关系
    这里以串行队列为例,打印它们继承链:
 id queueClass = object_getClass(serial);
    while (queueClass != nil) {
        NSLog(@"%@",queueClass);
        queueClass = [queueClass superclass];
    }

打印结果:

2021-08-22 12:24:12.708284+0800 GCDDemo[40888:22219369] OS_dispatch_queue_serial
2021-08-22 12:24:13.977272+0800 GCDDemo[40888:22219369] OS_dispatch_queue
2021-08-22 12:24:15.226658+0800 GCDDemo[40888:22219369] OS_dispatch_object
2021-08-22 12:24:16.567031+0800 GCDDemo[40888:22219369] OS_object
2021-08-22 12:24:17.128303+0800 GCDDemo[40888:22219369] NSObject

这里可以看出队列的基类也是NSObject。事实上所有GCD相关的对象基类都是NSObject。

  • 队列的底层结构
    GCD对象虽然是OC对象,但是它的底层结构体并不是直接使用NSObject,它有自己底层结构_os_object_s结构体:
typedef struct _os_object_s {
    _OS_OBJECT_HEADER(
    const _os_object_vtable_s *os_obj_isa,
    os_obj_ref_cnt,
    os_obj_xref_cnt);
} _os_object_s;

typedef struct _os_object_s *_os_object_t;

它也有isa指针os_obj_isa,也有对应的类,在os_obj_isa中:

typedef struct _os_object_vtable_s {
    _OS_OBJECT_CLASS_HEADER();
} _os_object_vtable_s;
//
static const _os_object_vtable_s _os_object_vtable;

同时它有自己的对象创建、内存管理等相关的方法:

_os_object_t
_os_object_alloc(const void *cls, size_t size);

_os_object_t
_os_object_alloc_realized(const void *cls, size_t size);

void _os_object_dealloc(_os_object_t object);

_os_object_t
_os_object_retain(_os_object_t object);

_os_object_t
_os_object_retain_with_resurrect(_os_object_t obj);

void
_os_object_release(_os_object_t object);

队列的底层结构也是以_os_object_s模板创建的。

队列的创建

通过查看dispatch_queue_create()函数源码了解队列创建过程。队列创建主要是在dispatch_lane_create_with_target()函数中实现的。它主要做一下几件事情:

  • 根据是DISPATCH_QUEUE_SERIAL或者DISPATCH_QUEUE_CONCURRENT创建一个dispatch_queue_attr_info_t结构体:
// 创建dqai,dqa表示DISPATCH_QUEUE_SERIAL或者DISPATCH_QUEUE_CONCURRENT
    dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);

//dqai的结构体定义
typedef struct dispatch_queue_attr_info_s {
    dispatch_qos_t dqai_qos : 8;
    int      dqai_relpri : 8;
    uint16_t dqai_overcommit:2;
    uint16_t dqai_autorelease_frequency:2;
    uint16_t dqai_concurrent:1;
    uint16_t dqai_inactive:1;
} dispatch_queue_attr_info_t;
  • 确定对象类型
    根据之前创建的dqai确定类型,DISPATCH_VTABLE宏定义中课获取它的类,这串行队列的类为OS_dispatch_queue_serial,而并行的为OS_dispatch_queue_concurrent:
    const void *vtable;
    dispatch_queue_flags_t dqf = legacy ? DQF_MUTABLE : 0;
    if (dqai.dqai_concurrent) {
        // OS_dispatch_queue_concurrent
        vtable = DISPATCH_VTABLE(queue_concurrent);
    } else {
                //OS_dispatch_queue_serial
        vtable = DISPATCH_VTABLE(queue_serial);
    }

通过DISPATCH_VTABLE拿到vtable(类信息):

#define DISPATCH_VTABLE(name) DISPATCH_OBJC_CLASS(name)
#define DISPATCH_OBJC_CLASS(name)   (&DISPATCH_CLASS_SYMBOL(name))
#define DISPATCH_CLASS_SYMBOL(name) OS_dispatch_##name##_class
  • 队列的alloc
    根据不同的类创建不同的队列queue:
 // alloc
    dispatch_lane_t dq = _dispatch_object_alloc(vtable,
            sizeof(struct dispatch_lane_s));
void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
#if OS_OBJECT_HAVE_OBJC1
    const struct dispatch_object_vtable_s *_vtable = vtable;
    dispatch_object_t dou;
    dou._os_obj = _os_object_alloc_realized(_vtable->_os_obj_objc_isa, size);
    dou._do->do_vtable = vtable;
    return dou._do;
#else
    return _os_object_alloc_realized(vtable, size);
#endif
}
inline _os_object_t
_os_object_alloc_realized(const void *cls, size_t size)
{
    _os_object_t obj;
    dispatch_assert(size >= sizeof(struct _os_object_s));
    while (unlikely(!(obj = calloc(1u, size)))) {
        _dispatch_temporary_resource_shortage();
    }
    obj->os_obj_isa = cls;
    return obj;
}

实际上创建一个_os_object_t结构体,并初始化isa指针,指向对应的类。

  • 队列init
// init
    _dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
            DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
            (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); 

初始化队列时会根据dqai.dqai_concurrent 判断是串行队列还是并行队列。这里是串行队列,所以最大并行数为1,如果是并行队列,那么最大并行数为DISPATCH_QUEUE_WIDTH_MAX:

#define DISPATCH_QUEUE_WIDTH_FULL           0x1000ull
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2)

DISPATCH_QUEUE_WIDTH_FULL的值为0x1000ull,16进制,转化成10进制就是4096,那DISPATCH_QUEUE_WIDTH_MAX=4096-2=4094,最大并行数4094。DISPATCH_QUEUE_WIDTH_POOL是全局并行队列的,它比普通并行队列的最大并行数大。

  • 创建并初始化队列完成之后设置label和优先级等其他信息,之后包装一下然后就返回了:
    dq->dq_label = label;
    dq->dq_priority = _dispatch_priority_make((dispatch_qos_t)dqai.dqai_qos,
            dqai.dqai_relpri);
    if (overcommit == _dispatch_queue_attr_overcommit_enabled) {
        dq->dq_priority |= DISPATCH_PRIORITY_FLAG_OVERCOMMIT;
    }
    if (!dqai.dqai_inactive) {
        _dispatch_queue_priority_inherit_from_target(dq, tq);
        _dispatch_lane_inherit_wlh_from_target(dq, tq);
    }
    _dispatch_retain(tq);
    dq->do_targetq = tq;
      return _dispatch_trace_queue_create(dq)._dq;
  • 主队列的创建
    主队列的是在程序启动的时候就被系统主动创建的,具体是在libdispatch_init方法里面被创建的,在main函数调用之前就创建了(点击了解iOS程序启动流程):
          //设置队列
    _dispatch_queue_set_current(&_dispatch_main_q);
        //绑定主线程
    _dispatch_queue_set_bound_thread(&_dispatch_main_q);

_dispatch_main_q是主队列的底层结构,它的结构体_dispatch_main_q数据结构如下:

struct dispatch_queue_static_s _dispatch_main_q = {
    DISPATCH_GLOBAL_OBJECT_HEADER(queue_main),
#if !DISPATCH_USE_RESOLVERS
    .do_targetq = _dispatch_get_default_queue(true),
#endif
    .dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(1) |
            DISPATCH_QUEUE_ROLE_BASE_ANON,
    .dq_label = "com.apple.main-thread",
    .dq_atomic_flags = DQF_THREAD_BOUND | DQF_WIDTH(1),
    .dq_serialnum = 1,
};

这里我们可以看到主线程的最大并行数width为1,也就是串行。而且主队列的序号dq_serialnum为1。

  • 全局并行队列的创建
    全局队列也是由系统创建的,它是一个集合。我们只能通过dispatch_get_global_queue()函数获取,而且只能获取对应的优先级的,如果我们随便传一个优先级,获取的queue就有可能为空。以下是demo演示:
dispatch_queue_t globQueue0 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t globQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    dispatch_queue_t globQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_queue_t globQueue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_queue_t customGlobQueue = dispatch_get_global_queue(1, 0);
    NSLog(@"\n-globQueue0:%@\n-globQueue1:%@\n-globQueue2:%@\n-globQueue3:%@\n-customGlobQueue:%@",globQueue0,globQueue1,globQueue2,globQueue3,customGlobQueue);

demo打印结果是:

-globQueue0:
-globQueue1:
-globQueue2:
-globQueue3:
-customGlobQueue:(null)

当我们传入优先级设置为1时,这时候它就找不到对应的队列,说明没有优先级为1的队列,所以这个值还是不要随便传。我们平时用的最多的是默认default=0优先级的队列,它的label是com.apple.root.default-qos,通过这个label我们到libdispatch库里面搜索源码:

struct dispatch_queue_global_s _dispatch_root_queues[] = {
#define _DISPATCH_ROOT_QUEUE_IDX(n, flags) \
        ((flags & DISPATCH_PRIORITY_FLAG_OVERCOMMIT) ? \
        DISPATCH_ROOT_QUEUE_IDX_##n##_QOS_OVERCOMMIT : \
        DISPATCH_ROOT_QUEUE_IDX_##n##_QOS)
#define _DISPATCH_ROOT_QUEUE_ENTRY(n, flags, ...) \
    [_DISPATCH_ROOT_QUEUE_IDX(n, flags)] = { \
        DISPATCH_GLOBAL_OBJECT_HEADER(queue_global), \
        .dq_state = DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE, \
        .do_ctxt = _dispatch_root_queue_ctxt(_DISPATCH_ROOT_QUEUE_IDX(n, flags)), \
        .dq_atomic_flags = DQF_WIDTH(DISPATCH_QUEUE_WIDTH_POOL), \
        .dq_priority = flags | ((flags & DISPATCH_PRIORITY_FLAG_FALLBACK) ? \
                _dispatch_priority_make_fallback(DISPATCH_QOS_##n) : \
                _dispatch_priority_make(DISPATCH_QOS_##n, 0)), \
        __VA_ARGS__ \
    }
    _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, 0,
        .dq_label = "com.apple.root.maintenance-qos",
        .dq_serialnum = 4,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(MAINTENANCE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.maintenance-qos.overcommit",
        .dq_serialnum = 5,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, 0,
        .dq_label = "com.apple.root.background-qos",
        .dq_serialnum = 6,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(BACKGROUND, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.background-qos.overcommit",
        .dq_serialnum = 7,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, 0,
        .dq_label = "com.apple.root.utility-qos",
        .dq_serialnum = 8,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(UTILITY, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.utility-qos.overcommit",
        .dq_serialnum = 9,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT, DISPATCH_PRIORITY_FLAG_FALLBACK,
        .dq_label = "com.apple.root.default-qos",
        .dq_serialnum = 10,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(DEFAULT,
            DISPATCH_PRIORITY_FLAG_FALLBACK | DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.default-qos.overcommit",
        .dq_serialnum = 11,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, 0,
        .dq_label = "com.apple.root.user-initiated-qos",
        .dq_serialnum = 12,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INITIATED, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.user-initiated-qos.overcommit",
        .dq_serialnum = 13,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, 0,
        .dq_label = "com.apple.root.user-interactive-qos",
        .dq_serialnum = 14,
    ),
    _DISPATCH_ROOT_QUEUE_ENTRY(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
        .dq_label = "com.apple.root.user-interactive-qos.overcommit",
        .dq_serialnum = 15,
    ),
};

这里可以看出,全局并行队列是一个集合。它们都是由系统创建的,它是个单利,所以要用dispatch_once_f函数来创建。:

static inline void
_dispatch_root_queues_init(void)
{
    dispatch_once_f(&_dispatch_root_queues_pred, NULL,
            _dispatch_root_queues_init_once);
}

在函数_dispatch_root_queues_init_once中进行初始化:

static void
_dispatch_root_queues_init_once(void *context DISPATCH_UNUSED)
{
    _dispatch_fork_becomes_unsafe();
#if DISPATCH_USE_INTERNAL_WORKQUEUE
    size_t i;
    for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) {
        _dispatch_root_queue_init_pthread_pool(&_dispatch_root_queues[i], 0,
                _dispatch_root_queues[i].dq_priority);
    }
#else
    int wq_supported = _pthread_workqueue_supported();
    int r = ENOTSUP;

    if (!(wq_supported & WORKQ_FEATURE_MAINTENANCE)) {
        DISPATCH_INTERNAL_CRASH(wq_supported,
                "QoS Maintenance support required");
    }

#if DISPATCH_USE_KEVENT_SETUP
    struct pthread_workqueue_config cfg = {
        .version = PTHREAD_WORKQUEUE_CONFIG_VERSION,
        .flags = 0,
        .workq_cb = 0,
        .kevent_cb = 0,
        .workloop_cb = 0,
        .queue_serialno_offs = dispatch_queue_offsets.dqo_serialnum,
#if PTHREAD_WORKQUEUE_CONFIG_VERSION >= 2
        .queue_label_offs = dispatch_queue_offsets.dqo_label,
#endif
    };
#endif

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunreachable-code"
    if (unlikely(!_dispatch_kevent_workqueue_enabled)) {
#if DISPATCH_USE_KEVENT_SETUP
        cfg.workq_cb = _dispatch_worker_thread2;
        r = pthread_workqueue_setup(&cfg, sizeof(cfg));
#else
        r = _pthread_workqueue_init(_dispatch_worker_thread2,
                offsetof(struct dispatch_queue_s, dq_serialnum), 0);
#endif // DISPATCH_USE_KEVENT_SETUP
#if DISPATCH_USE_KEVENT_WORKLOOP
    } else if (wq_supported & WORKQ_FEATURE_WORKLOOP) {
#if DISPATCH_USE_KEVENT_SETUP
        cfg.workq_cb = _dispatch_worker_thread2;
        cfg.kevent_cb = (pthread_workqueue_function_kevent_t) _dispatch_kevent_worker_thread;
        cfg.workloop_cb = (pthread_workqueue_function_workloop_t) _dispatch_workloop_worker_thread;
        r = pthread_workqueue_setup(&cfg, sizeof(cfg));
#else
        r = _pthread_workqueue_init_with_workloop(_dispatch_worker_thread2,
                (pthread_workqueue_function_kevent_t)
                _dispatch_kevent_worker_thread,
                (pthread_workqueue_function_workloop_t)
                _dispatch_workloop_worker_thread,
                offsetof(struct dispatch_queue_s, dq_serialnum), 0);
#endif // DISPATCH_USE_KEVENT_SETUP
#endif // DISPATCH_USE_KEVENT_WORKLOOP
#if DISPATCH_USE_KEVENT_WORKQUEUE
    } else if (wq_supported & WORKQ_FEATURE_KEVENT) {
#if DISPATCH_USE_KEVENT_SETUP
        cfg.workq_cb = _dispatch_worker_thread2;
        cfg.kevent_cb = (pthread_workqueue_function_kevent_t) _dispatch_kevent_worker_thread;
        r = pthread_workqueue_setup(&cfg, sizeof(cfg));
#else
        r = _pthread_workqueue_init_with_kevent(_dispatch_worker_thread2,
                (pthread_workqueue_function_kevent_t)
                _dispatch_kevent_worker_thread,
                offsetof(struct dispatch_queue_s, dq_serialnum), 0);
#endif // DISPATCH_USE_KEVENT_SETUP
#endif
    } else {
        DISPATCH_INTERNAL_CRASH(wq_supported, "Missing Kevent WORKQ support");
    }
#pragma clang diagnostic pop

    if (r != 0) {
        DISPATCH_INTERNAL_CRASH((r << 16) | wq_supported,
                "Root queue initialization failed");
    }
#endif // DISPATCH_USE_INTERNAL_WORKQUEUE
}

同步、异步

在iOS中,队列的调度主要由两个函数dispatch_async()(异步)和dispatch_sync()(同步),它们负责将block添加到对应的队列中,然后在适当的时候回调block。

异步函数-dispatch_async

异步函数dispatch_async的作用是可以异步添加block到派发队列中。简单来说就是调用dispatch_async不会阻塞当前线程,block添加到队列中之后立即返回,不需要等到block执行完成。使用示例:

dispatch_async(mainQueue, ^{
       NSLog(@"1");
   });
  • 底层原理
    通过底层源码来分析dispatch_async的实现原理:
void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DC_FLAG_CONSUME;
    dispatch_qos_t qos;

    // 任务包装器 
    qos = _dispatch_continuation_init(dc, dq, work, 0, dc_flags);
    _dispatch_continuation_async(dq, dc, qos, dc->dc_flags);
}

创建一个任务包装器,用于接收并保存当前的任务块block,通过函数式保存下来,等到合适的时机调用:

DISPATCH_ALWAYS_INLINE
static inline dispatch_qos_t
_dispatch_continuation_init(dispatch_continuation_t dc,
        dispatch_queue_class_t dqu, dispatch_block_t work,
        dispatch_block_flags_t flags, uintptr_t dc_flags)
{
    void *ctxt = _dispatch_Block_copy(work);

    dc_flags |= DC_FLAG_BLOCK | DC_FLAG_ALLOCATED;
    if (unlikely(_dispatch_block_has_private_data(work))) {
        dc->dc_flags = dc_flags;
        dc->dc_ctxt = ctxt;
        // will initialize all fields but requires dc_flags & dc_ctxt to be set
        return _dispatch_continuation_init_slow(dc, dqu, flags);
    }
    dispatch_function_t func = _dispatch_Block_invoke(work);
    if (dc_flags & DC_FLAG_CONSUME) {
        func = _dispatch_call_block_and_release;
    }
    return _dispatch_continuation_init_f(dc, dqu, ctxt, func, flags, dc_flags);
}

然后把任务包装器添加到队列中去,然后立即返回:

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_continuation_async(dispatch_queue_class_t dqu,
        dispatch_continuation_t dc, dispatch_qos_t qos, uintptr_t dc_flags)
{
#if DISPATCH_INTROSPECTION
    if (!(dc_flags & DC_FLAG_NO_INTROSPECTION)) {
        _dispatch_trace_item_push(dqu, dc);
    }
#else
    (void)dc_flags;
#endif
    return dx_push(dqu._dq, dc, qos);
}

这里dx_push会把block添加到对应的队列之中,后面的流程 _dispatch_continuation_async -> dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z),这里dq_push是一个函数指针:

#define DISPATCH_QUEUE_VTABLE_HEADER(x); \
    DISPATCH_OBJECT_VTABLE_HEADER(x); \
    void (*const dq_activate)(dispatch_queue_class_t); \
    void (*const dq_wakeup)(dispatch_queue_class_t, dispatch_qos_t, \
            dispatch_wakeup_flags_t); \
    void (*const dq_push)(dispatch_queue_class_t, dispatch_object_t, \
            dispatch_qos_t)

void (*const dq_push)是个函数指针,queue子类的实现不一样,根据不同的子类函数实现不一样。以下是几个常用的queue的dq_push实现(源码具体在init.c文件中):

DISPATCH_NOINLINE
static void
_dispatch_object_no_invoke(dispatch_object_t dou,
        DISPATCH_UNUSED dispatch_invoke_context_t dic,
        DISPATCH_UNUSED dispatch_invoke_flags_t flags)
{
    DISPATCH_INTERNAL_CRASH(dx_type(dou._do), "do_invoke called");
}

/*
 * Dispatch queue cluster
 */

DISPATCH_NOINLINE
static void
_dispatch_queue_no_activate(dispatch_queue_class_t dqu)
{
    DISPATCH_INTERNAL_CRASH(dx_type(dqu._dq), "dq_activate called");
}

DISPATCH_VTABLE_INSTANCE(queue,
    // This is the base class for queues, no objects of this type are made
    .do_type        = _DISPATCH_QUEUE_CLUSTER,
    .do_dispose     = _dispatch_object_no_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_object_no_invoke,
    .dq_activate    = _dispatch_queue_no_activate,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
    .do_type        = DISPATCH_QUEUE_SERIAL_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
    .do_type        = DISPATCH_QUEUE_CONCURRENT_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_concurrent_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
    .do_type        = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
    .do_dispose     = _dispatch_object_no_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_object_no_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_root_queue_wakeup,
    .dq_push        = _dispatch_root_queue_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
    .do_type        = DISPATCH_QUEUE_MAIN_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_main_queue_wakeup,
    .dq_push        = _dispatch_main_queue_push,
);

这里我们看到dq_push函数每个子类是不一样的。后面会分别分析。
备注:异步函数具有开启新的线程的能力,但是不一定会开启新的线程,比如异步函数执行主队列:

dispatch_async(mainQueue, ^{
       NSLog(@"1");
   });

上面的demo时不会开启新的线程的,因为主队列只能在主线程执行。

同步函数-dispatch_sync

同步函数dispatch_sync的作用是可以同步添加block到派发队列中。简单来说就是调用dispatch_sync会阻塞当前线程,block添加到队列中之后不会立即返回,而是需要等到block执行完成才能返回,并继续执行后面的代码。

  • 使用示例:
dispatch_async(queue, ^{
       NSLog(@"1");
   });
  • 调用流程
    dispatch_sync->_dispatch_sync_f -> _dispatch_sync_f_inline->
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sync_f_inline(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func, uintptr_t dc_flags)
{
    if (likely(dq->dq_width == 1)) {
        return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
    }

    if (unlikely(dx_metatype(dq) != _DISPATCH_LANE_TYPE)) {
        DISPATCH_CLIENT_CRASH(0, "Queue type doesn't support dispatch_sync");
    }

    dispatch_lane_t dl = upcast(dq)._dl;
    // Global concurrent queues and queues bound to non-dispatch threads
    // always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
    if (unlikely(!_dispatch_queue_try_reserve_sync_width(dl))) {
        return _dispatch_sync_f_slow(dl, ctxt, func, 0, dl, dc_flags);
    }

    if (unlikely(dq->do_targetq->do_targetq)) {
        return _dispatch_sync_recurse(dl, ctxt, func, dc_flags);
    }
    _dispatch_introspection_sync_begin(dl);
    _dispatch_sync_invoke_and_complete(dl, ctxt, func DISPATCH_TRACE_ARG(
            _dispatch_trace_item_sync_push_pop(dq, ctxt, func, dc_flags)));
}

到这里的话会根据不同的队列类型进行调用,比如说会区分是串行队列、或者是并行队列。这个在后面的同步串行和同步并行部分继续分析。
备注:同步会阻塞当前线程,但不一定在当前线程执行队列任务,比如下面的代码:

dispatch_queue_t conque1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    NSLog(@"1");
    dispatch_async(conque1, ^{
        NSLog(@"2");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"3");
        });
    });
    NSLog(@"4");

上面代码中,在子线程同步执行主队列,主队列会交给主线线程执行,执行完block之后再回到当前子线程执行下面的代码。

队列、函数和线程的关系

有了队列串行和并行,函数的同步异步,我们就可以把它们之间的使用组合总结为同步串行、同步并行、异步串行和异步并行。但是由于前面说过的主队列和全局并行队列的特殊性,在使用上还要细分,接下来一个个分析。

同步串行

代码示例:

- (void)serialSyncTest{
    //创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.hello-world.dongjianxiong", DISPATCH_QUEUE_SERIAL);
   
    NSLog(@"1");
    dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"2");
    });
    NSLog(@"3");
}

这段代码的执行流程是1 -> 2 -> 3。
在这里dispatch_sync()调用流程是:

dispatch_sync -> _dispatch_sync_f -> _dispatch_sync_f_inline -> _dispatch_barrier_sync_f->_dispatch_barrier_sync_f_inline -> _dispatch_lane_barrier_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline:

  • 在函数_dispatch_sync_f_inline()中判断是否是串行队列,这里是同步串行,所以走_dispatch_barrier_sync_f
if (likely(dq->dq_width == 1)) {
        return _dispatch_barrier_sync_f(dq, ctxt, func, dc_flags);
    }
  • 函数_dispatch_barrier_sync_f_inline这里会尝试获取同步锁,判断是继续执行,还是走_dispatch_sync_f_slow流程:
static inline void
_dispatch_barrier_sync_f_inline(dispatch_queue_t dq, void *ctxt,
        dispatch_function_t func, uintptr_t dc_flags)
{
    dispatch_tid tid = _dispatch_tid_self();

    if (unlikely(dx_metatype(dq) != _DISPATCH_LANE_TYPE)) {
        DISPATCH_CLIENT_CRASH(0, "Queue type doesn't support dispatch_sync");
    }

    dispatch_lane_t dl = upcast(dq)._dl;
    // The more correct thing to do would be to merge the qos of the thread
    // that just acquired the barrier lock into the queue state.
    //
    // However this is too expensive for the fast path, so skip doing it.
    // The chosen tradeoff is that if an enqueue on a lower priority thread
    // contends with this fast path, this thread may receive a useless override.
    //
    // Global concurrent queues and queues bound to non-dispatch threads
    // always fall into the slow case, see DISPATCH_ROOT_QUEUE_STATE_INIT_VALUE
    if (unlikely(!_dispatch_queue_try_acquire_barrier_sync(dl, tid))) {
        return _dispatch_sync_f_slow(dl, ctxt, func, DC_FLAG_BARRIER, dl,
                DC_FLAG_BARRIER | dc_flags);
    }

    if (unlikely(dl->do_targetq->do_targetq)) {
        return _dispatch_sync_recurse(dl, ctxt, func,
                DC_FLAG_BARRIER | dc_flags);
    }
    _dispatch_introspection_sync_begin(dl);
    _dispatch_lane_barrier_sync_invoke_and_complete(dl, ctxt, func
            DISPATCH_TRACE_ARG(_dispatch_trace_item_sync_push_pop(
                    dq, ctxt, func, dc_flags | DC_FLAG_BARRIER)));
}

函数中尝试获得锁(dispatch_queue_try_acquire_barrier_sync),如果获取成功,进入_dispatch_introspection_sync_begin(dl)对队列进行调整,比如根据优先级调整循序等。然后进行block调用:
_dispatch_lane_barrier_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline:

DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_sync_function_invoke_inline(dispatch_queue_class_t dq, void *ctxt,
        dispatch_function_t func)
{
    dispatch_thread_frame_s dtf;
    _dispatch_thread_frame_push(&dtf, dq);
    _dispatch_client_callout(ctxt, func);
    _dispatch_perfmon_workitem_inc();
    _dispatch_thread_frame_pop(&dtf);
}

接着在函数_dispatch_sync_function_invoke_inline()中进行push队列,然后在当前线程中调用block(_dispatch_client_callout),调用完成之后然后pop出列。

  • 死锁的原因
    如果前面获取同步锁失败,则会进入_dispatch_sync_f_slow->DISPATCH_WAIT_FOR_QUEUE流程,在这里等待锁的释放。在这里会校验当前的队列和要同步的队列是否同一个,如果是就抛出死锁崩溃信息:
uint64_t dq_state = _dispatch_wait_prepare(dq);
    if (unlikely(_dq_state_drain_locked_by(dq_state, dsc->dsc_waiter))) {
        DISPATCH_CLIENT_CRASH((uintptr_t)dq_state,
                "dispatch_sync called on queue "
                "already owned by current thread");
    }
  • block调用
    串行队列:_dispatch_lane_barrier_sync_invoke_and_complete->_dispatch_client_callout
    主队列由runloop调用:
    __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__->_dispatch_main_queue_callback_4CF->_dispatch_client_callout->_dispatch_async_and_wait_invoke->_dispatch_client_callout
同步并行

代码示例:

- (void)concurrentSyncTest{
    //创建并行队列
    dispatch_queue_t queue = dispatch_queue_create("com.hello-world.dongjianxiong", DISPATCH_QUEUE_CONCURRENT);
   NSLog(@"1");
   dispatch_sync(queue, ^{
            NSLog(@"%d-%@",i,[NSThread currentThread]);
           NSLog(@"2");
        });
    NSLog(@"3");
}

代码执行顺序:1->2->3,与同步串行一样。

  • 普通队列同步并行dispatch_sync()调用流程是:

dispatch_sync -> _dispatch_sync_f ->_dispatch_sync_f_inline ->_dispatch_sync_invoke_and_complete -> _dispatch_sync_function_invoke_inline

  • 全局队列同步并行dispatch_sync()调用流程是
    代码示例:
- (void)globalSyncTest{
    
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    NSLog(@"1");
    dispatch_sync(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"2");
    });
    NSLog(@"3");
}

dispatch_sync -> _dispatch_sync_f->_dispatch_sync_f_inline->_dispatch_sync_f_slow ->__DISPATCH_WAIT_FOR_QUEUE__ -> dx_push
globalQueue会交由dx_push添加到队列中,然后等待系统线程池分配线程执行block。

  • block 调用
    同步并行:_dispatch_sync_invoke_and_complete->_dispatch_client_callout
    全局同步并行:_dispatch_sync_function_invoke->_dispatch_client_callout
异步串行

代码示例:

- (void)serialAsyncTest{
    //创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.hello-world.dongjianxiong", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        NSLog(@"2");
    });
    NSLog(@"3");
}

代码执行顺序:1->3->2。这里的block不会阻塞3的执行,它可能在新的线程执行的,也可能不是。

dispatch_async()调用流程:

dispatch_async->_dispatch_continuation_async-> dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_serial, lane,
    .do_type        = DISPATCH_QUEUE_SERIAL_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_push,
);
DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_main, lane,
    .do_type        = DISPATCH_QUEUE_MAIN_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_main_queue_wakeup,
    .dq_push        = _dispatch_main_queue_push,
);

普通串行队列_dispatch_lane_push(dq_push)->dx_wakeup(x, y, z) dx_vtable(x)->_dispatch_lane_wakeup(dq_wakeup) ->_dispatch_queue_wakeup->
主队列:_dispatch_lane_push(dq_push)->dx_wakeup(x, y, z) dx_vtable(x)->_dispatch_main_queue_wakeup(dq_wakeup)->_dispatch_runloop_queue_wakeup`

void
_dispatch_main_queue_wakeup(dispatch_queue_main_t dq, dispatch_qos_t qos,
        dispatch_wakeup_flags_t flags)
{
#if DISPATCH_COCOA_COMPAT
    if (_dispatch_queue_is_thread_bound(dq)) {
        return _dispatch_runloop_queue_wakeup(dq->_as_dl, qos, flags);
    }
#endif
    return _dispatch_lane_wakeup(dq, qos, flags);
}

由于全局队列绑定到主线程,所以任务会被添加到runloop中,等待runloop调用。

  • block调用
    串行队列:_dispatch_workloop_worker_thread->_dispatch_lane_invoke->_dispatch_lane_serial_drain->_dispatch_client_callout->_dispatch_call_block_and_release
    主队列由runloop调用:
    __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__->_dispatch_main_queue_callback_4CF->_dispatch_client_callout->_dispatch_call_block_and_release
异步并行

代码示例:

- (void)concurrentAsyncTest{
    //创建并行队列
//    dispatch_queue_t queue = dispatch_queue_create("com.hello-world.dongjianxiong", DISPATCH_QUEUE_CONCURRENT);
    //创建全局并行队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        NSLog(@"%@",[NSThread currentThread]);
    });
    NSLog(@"3");
}

代码执行流程:1->3->2。这里的block不会阻塞3的执行,它可能在新的线程执行的,也可能不是。

dispatch_async()调用流程:

dispatch_async->_dispatch_continuation_async-> dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_concurrent, lane,
    .do_type        = DISPATCH_QUEUE_CONCURRENT_TYPE,
    .do_dispose     = _dispatch_lane_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_lane_invoke,

    .dq_activate    = _dispatch_lane_activate,
    .dq_wakeup      = _dispatch_lane_wakeup,
    .dq_push        = _dispatch_lane_concurrent_push,
);

DISPATCH_VTABLE_SUBCLASS_INSTANCE(queue_global, lane,
    .do_type        = DISPATCH_QUEUE_GLOBAL_ROOT_TYPE,
    .do_dispose     = _dispatch_object_no_dispose,
    .do_debug       = _dispatch_queue_debug,
    .do_invoke      = _dispatch_object_no_invoke,

    .dq_activate    = _dispatch_queue_no_activate,
    .dq_wakeup      = _dispatch_root_queue_wakeup,
    .dq_push        = _dispatch_root_queue_push,
);

对于全局并行队列,dq_push = _dispatch_root_queue_push, _dispatch_root_queue_push的流程:

_dispatch_root_queue_push_inline->_dispatch_root_queue_push->_dispatch_root_queue_poke->_dispatch_root_queue_poke_slow

全局并行队列流程会在_dispatch_root_queue_poke_slow()函数中选择一个线程来执行当前的block,如果线程池的线程不够用,则创建新的线程。同时第一次进来会对全局变量进行初始化:

_dispatch_root_queues_init();
  • block 调用
    并行队列:_dispatch_worker_thread2->_dispatch_root_queue_drain->_dispatch_async_redirect_invoke->_dispatch_continuation_pop->_dispatch_client_callout->_dispatch_call_block_and_release
    全局并行队列:_dispatch_worker_thread2->_dispatch_root_queue_drain->_dispatch_queue_override_invoke->_dispatch_client_callout->_dispatch_call_block_and_release

你可能感兴趣的:(GCD的队列和线程的关系-GCD源码学习笔记)