二十、函数与队列
GCD
什么是GCD?
将任务添加到队列,并且指定执⾏任务的函数
全称是 Grand Central Dispatch
纯 C 语⾔,提供了⾮常多强⼤的函数
GCD 这块已经开源(开源地址)。
GCD的优势
- GCD 是苹果公司为多核的并⾏运算提出的解决⽅案
- GCD 会⾃动利⽤更多的CPU内核(⽐如双核、四核)
- GCD 会⾃动管理线程的⽣命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉 GCD 想要执⾏什么任务,不需要编写任何线程管理代码
函数
- 任务使⽤ block 封装
- 任务的 block 没有参数也没有返回值
- 执⾏任务的函数
- 异步
dispatch_async
- 不⽤等待当前语句执⾏完毕,就可以执⾏下⼀条语句
- 会开启线程执⾏ block 的任务
- 异步是多线程的代名词
- 同步
dispatch_sync
- 必须等待当前语句执⾏完毕,才会执⾏下⼀条语句
- 不会开启线程
- 在当前执⾏ block 的任务
- 异步
队列

GCD 中的使用 FIFO
队列,用来保证先进来的任务先得到执行,这种队列称之为 dispatch queue。队列一共有两种类型,分别如下:
-
串行队列(Serial queue):又叫 private dispatch queues,同时只执行一个任务(单独的 block 块),常用于同步访问特定的资源或数据。当创建多个 serial queue 时,虽然各自是同步,但 serial queue 之间是并发执行的。
-
并行队列(Concurrent queue):又叫 global dispatch queue,可以并发的执行多个任务(多个 block 块),但执行完成顺序是随机的。
- 同步函数串行队列
- 不会开启线程,在当前线程执行任务
- 任务串行执行,任务一个接着一个
- 会产生堵寨
- 同步函数并发队列
- 不会开启线程,在当前线程执行任务
- 任务一个接着一个
- 异步函数串行队列
- 开启线程一条新线程
- 任务一个接着一个
- 异步函数并发队列
- 开启线程,在当前线程执行任务
- 任务异步执行,没有顺序,CPU调度有关
而同步异步都会消耗时间去执行,并且,异步消耗的时间比同步多
面试题
dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
// 耗时
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_async(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
因为这里的是异步并发,所以都会消耗时间开辟线程,而且不会堵塞队列,所以一般情况下(可能会有线程卡顿堵塞情况)最后的顺序是15243。
// 同步队列
dispatch_queue_t queue = dispatch_queue_create("cooci", NULL);
NSLog(@"1");
// 异步函数
dispatch_async(queue, ^{
NSLog(@"2");
// 同步
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
使用dispatch_async 调用一个block,这个block会被放到指定的queue队尾等待执行,至于这个block是并行还是串行执行只和dispatch_async参数里面指定的queue是并行和串行有关。但是dispatch_async会马上返回。
使用dispatch_sync 同样也是把block放到指定的queue上面执行,但是会等待这个block执行完毕才会返回,阻塞当前queue直到sync函数返回。
当前queue上调用sync函数,并且sync函数中指定的queue也是当前queue。需要执行的block被放到当前queue的队尾等待执行,因为这是一个串行的queue,
调用sync函数会阻塞当前队列,等待block执行 -> 这个block永远没有机会执行 -> sync函数不返回,所以当前队列就永远被阻塞了,这就造成了死锁。
dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{ // 耗时
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"0");
dispatch_async(queue, ^{
NSLog(@"7");
});
dispatch_async(queue, ^{
NSLog(@"8");
});
dispatch_async(queue, ^{
NSLog(@"9");
});
因为0在主线程执行的,所以7、8、9在0之后执行;3是同步函数,会产生堵塞,1、2是异步并发,所以1、2、3顺序是随机的
主队列和全局并发队列
主队列
dispatch_get_main_queue();
- 专⻔⽤来在主线程上调度任务的串⾏队列
- 不会开启线程
- 如果当前主线程正在有任务执⾏,那么⽆论主队列中当前被添加了什么任务,都不会被调度
Returns the main queue. This queue is created automatically on behalf of the main thread before main() is called.
返回主队列。这个队列是在调用main()之前代表主线程自动创建的。
全局并发队列
- 为了⽅便程序员的使⽤,苹果提供了全局队列 dispatch_get_global_queue(0, 0)
- 全局队列是⼀个并发队列
- 在使⽤多线程开发时,如果对队列没有特殊需求,在执⾏异步任务时,可以直接使⽤全局队列
dispatch_queue_t serial = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t conque = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 多个 - 集合
dispatch_queue_t globQueue = dispatch_get_global_queue(0, 0);
NSLog(@"%@-%@-%@-%@",serial,conque,mainQueue,globQueue);
···················
<OS_dispatch_queue_serial: cooci>-<OS_dispatch_queue_concurrent: cooci>-<OS_dispatch_queue_main: com.apple.main-thread>-<OS_dispatch_queue_global: com.apple.root.default-qos>
···················
我们可以看到mainQueue的线程名字为com.apple.main-thread
,我们在开源代码里搜索
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,
};
在到初始化函数中查看
void
libdispatch_init(void)
{
...
#if DISPATCH_USE_RESOLVERS // rdar://problem/8541707
_dispatch_main_q.do_targetq = _dispatch_get_default_queue(true);
#endif
_dispatch_queue_set_current(&_dispatch_main_q);//设置主队列
_dispatch_queue_set_bound_thread(&_dispatch_main_q);//绑定到线程中
#if DISPATCH_USE_PTHREAD_ATFORK
(void)dispatch_assume_zero(pthread_atfork(dispatch_atfork_prepare,
dispatch_atfork_parent, dispatch_atfork_child));
#endif
_dispatch_hw_config_init();
_dispatch_time_init();
_dispatch_vtable_init();
_os_object_init();
_voucher_init();
_dispatch_introspection_init();
}
dispatch_get_global_queue
可以传参,参数是服务质量,所以全局并发有多个,有个集合来收集,搜索
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_queue_create
全局搜索 dispatch_queue_create(con
static dispatch_queue_t
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
// dqai 创建 -
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
......
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 {
vtable = DISPATCH_VTABLE(queue_serial);
}
...
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s)); // alloc
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0)); // init
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;
_dispatch_object_debug(dq, "%s", __func__);
return _dispatch_trace_queue_create(dq)._dq;
}
dispatch_queue_class_t
_dispatch_introspection_queue_create(dispatch_queue_t dq)
{
...
dqic = _dispatch_calloc(1, sz);
dqic->dqic_queue._dq = dq;
if (_dispatch_introspection.debug_queue_inversions) {
LIST_INIT(&dqic->dqic_order_top_head);
LIST_INIT(&dqic->dqic_order_bottom_head);
}
dq->do_finalizer = dqic;
...
return upcast(dq)._dqu;
}
DISPATCH_ALWAYS_INLINE
static inline dispatch_object_t
upcast(dispatch_object_t dou)
{
return dou;
}
所以我们直接看dq
dispatch_queue_attr_info_t
_dispatch_queue_attr_to_info(dispatch_queue_attr_t dqa)
{
dispatch_queue_attr_info_t dqai = { };
if (!dqa) return dqai;
#if DISPATCH_VARIANT_STATIC
if (dqa == &_dispatch_queue_attr_concurrent) { // null 默认
dqai.dqai_concurrent = true;
return dqai;
}
...
}
dqai.dqai_concurrent ?DISPATCH_QUEUE_WIDTH_MAX : 1,
在这里就决定了到底是并发还是串行。
所以最大的并发容积为 DISPATCH_QUEUE_WIDTH_MAX
#define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull//4096
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2)
DISPATCH_QUEUE_WIDTH_POOL
就是上面全局并发队列的数量
vtable = DISPATCH_VTABLE
指向了OS_dispatch_##name
和我们用 object_getclass
或取的一样
void *
_dispatch_object_alloc(const void *vtable, size_t size)
{
#if OS_OBJECT_HAVE_OBJC1
...
#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;
}
最后isa指向的就是vtable