一、多线程理解
- 进程是操作系统的最小执行单元,线程是进程执行的最小单元,iOS是单进程,多线程执行的,进程之间资源不共享。
- 多线程可以提高程序的执行效率,其实是提高了资源的利用率,对于单核CPU来讲,通过在不同任务之间切换来达到任务同时执行的假象,对于多核CPU来讲 才是真正的并发。GCD 正是充分的利用了多核cpu
二、GCD
grand central dispatch 自动管理线程的生命周期
- 串行队列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
- 并行队列
dispatch_queue_t concurrentQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_CONCURRENT);
- 串行队列 同步执行 阻塞当前线程 不开启新线程
dispatch_sync(serialQueue _Nonnull queue, ^{
})
- 串行队列异步执行 不阻塞当前线程 会开辟一条新线程
dispatch_async(serialQueue _Nonnull queue, ^{
})
- 并行队列同步执行 阻塞当前线程 不开启新线程 同 3
- 并行队列异步执行 不阻塞当前线程 开启新线程 ,理论上讲每dispatch_async一次都会出现一条新线程 但是系统会根据当前线程池的情况 重用以前创建过的线程。
dispatch_async(concurrentQueue, ^{
});
- 全局队列 dispatch_get_global_queue 是并行队列
- 主队列 dispatch_get_main_queue 是串行队列
- 加锁 之 信号量 dispatch_semaphore_t
- 栅栏函数 是阻塞队列 所以必须用在同一个队列中的 同时栅栏函数不能用全局队列 原因很简单 不能阻塞全局队列吧
dispatch_barrier_async(concurrentQueue, ^{
});
dispatch_semaphore_t sem = dispatch_semaphore_create(1);
for (int i = 0; i < 10; i ++){
dispatch_async(concurrentQueue, ^{
NSLog(@"--sem 输出-----%d",i);
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
}
如果不加锁的话 0-9是乱序输出的。加上锁之后就是按照0-9的顺序输出
三、经典面试题
12. 下面代码的输出顺序
dispatch_async(concurrentQueue, ^{
NSLog(@"1");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"2");
});
dispatch_sync(concurrentQueue, ^{
NSLog(@"3");
});
NSLog(@"0");
dispatch_async(concurrentQueue, ^{
NSLog(@"4");
});
dispatch_async(concurrentQueue, ^{
NSLog(@"5");
});
dispatch_sync(concurrentQueue, ^{
NSLog(@"6");
});
解释 输出顺序 肯定是3 在0前边 12 在3前或者3后 顺序不定。456是最后三个 顺序不定
- 下面代码会如何执行 死锁
dispatch_async(serialQueue, ^{
NSLog(@"111");
dispatch_sync(serialQueue, ^{
NSLog(@"222");
});
});
解释 serialQueue 是串行队列 任务是顺序执行的 此时队列中的任务是这样的 (1)NSLog(@“111”); — (2)dispatch_sync(serialQueue, ^{ }); ----(3)NSLog(@“222”); 串行队列遵循先进先出 必须要等(2)执行完才能继续执行 但是(2)执行完有必须等(3)执行完 但是(3)又得不到执行的机会 所以相互等待 死锁
四、底层分析
- 队列是如何创建的
GCD的源码在libdispatch库中 通过源码一步一步调用来到
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
return _dispatch_lane_create_with_target(label, attr,
DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
_dispatch_lane_create_with_target 核心代码
_dispatch_lane_create_with_target(const char *label, dispatch_queue_attr_t dqa,
dispatch_queue_t tq, bool legacy)
{
dispatch_queue_attr_info_t dqai = _dispatch_queue_attr_to_info(dqa);
// 开辟内存 - 生成响应的对象 queue
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));
if (!tq) {
tq = _dispatch_get_root_queue(
qos == DISPATCH_QOS_UNSPECIFIED ? DISPATCH_QOS_DEFAULT : qos, // 4
overcommit == _dispatch_queue_attr_overcommit_enabled)->_as_dq; // 0 1
if (unlikely(!tq)) {
DISPATCH_CLIENT_CRASH(qos, "Invalid queue attribute");
}
}
// 开辟内存 - 生成响应的对象 queue
dispatch_lane_t dq = _dispatch_object_alloc(vtable,
sizeof(struct dispatch_lane_s));
// 构造方法
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ?
DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER |
(dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
// 标签
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;// 这里的 do_targetq是通过 _dispatch_get_root_queue 获得的
_dispatch_object_debug(dq, "%s", __func__);
return _dispatch_trace_queue_create(dq)._dq;
}
总结:当创建队列的时候 会调用_dispatch_lane_create_with_target 然后开辟内存 初始化 设置优先级 然后从 root_queue 中获取队列 赋值给do_targetq