目录引言进程与线程1.进程的定义2.线程的定义3、 进程和线程的关系4、 多线程5、 时间片6、 线程池GCD1、任务2、队列3、死锁总结引言 在iOS开发过程中,绕不开网络请求、下
在iOS开发过程中,绕不开网络请求、下载图片之类的耗时操作,这些操作放在主线程中处理会造成卡顿现象,所以我们都是放在子线程进行处理,处理完成后再返回到主线程进行展示。
多线程贯穿了我们整个的开发过程,ioS的多线程操作有NSThread、GCD、NSOperation,其中我们最常用的就是GCD。
在了解GCD之前我们先来了解一下进程和线程,及他们的联系与区别
同一时间,CPU只能处理1条线程,只有1条线程在执行。多线程并发执行,其实是CPU快速地在多条线程之间调度(切换)。如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
如果线程非常非常多,CPU会在N多线程之间调度,消耗大量的CPU资源,每条线程被调度执行的频次会降低(线程的执行效率降低)
多线程的优点:
多线程的缺点:
多线程的生命周期是:新建 - 就绪 - 运行 - 阻塞 - 死亡
时间片:CPU在多个任务直接进行快速的切换,这个时间间隔就是时间片
设备并发执行的数量是有限的,使用[NSProcessInfo processInfo].activeProcessorCount可以查看当前设备能够支持线程的最大并发数量,比如说最大并发数是8,代表8核cpu,如果同时开启了10个线程,则会有CPU通过时间片轮转的方式让某一个或者某两个线程分别执行一段时间。
GCD在内部维护了一个线程池,目的是为了复用线程,需要开启线程时,其会先在线程池中查询已开辟的空闲线程缓存,达到节省内存空间和时间的目的。
GCD全称是Grand Central Dispatch,它是纯 C 语言,并且提供了非常多强大的函数
GCD的优势:
就是执行操作的意思,也就是在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式:同步执行(sync)和异步执行(async)
队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。队列的作用就是存储任务,和线程没有任何关系。每读取一个任务,则从队列中释放一个任务
在 GCD 中有两种队列:串行队列和并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。
在串行队列中添加任务的block中含有另一个向同一队列添加的同步任务就会发生死锁,因为同步任务立即执行,队列需要遵守FIFO(先进先出)的原则,串行队列需要等待前一个任务执行结束才会执行下一个任务,导致互相等待造成死锁。
接下来我们从源码中了解一下GCD的串行队列和并发队列都做了什么,有什么区别。
dispatch_queue_t main = dispatch_get_main_queue(); //主队列
dispatch_queue_t global = dispatch_get_global_queue(0, 0); //全局队列
dispatch_queue_t serial = dispatch_queue_create("WT", DISPATCH_QUEUE_SERIAL); // 串行队列
dispatch_queue_t concurrent = dispatch_queue_create("WT", DISPATCH_QUEUE_CONCURRENT); //并发队列
// 主队列源码
dispatch_get_main_queue(void) {
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _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,
};
// 全局队列源码
dispatch_get_global_queue(intptr_t priority, uintptr_t flags) {
dispatch_qos_t qos = _dispatch_qos_from_queue_priority(priority);
……
return _dispatch_get_root_queue(qos, flags & DISPATCH_QUEUE_OVERCOMMIT);
}
#define DISPATCH_QUEUE_WIDTH_FULL 0x1000ull
#define DISPATCH_QUEUE_WIDTH_POOL (DISPATCH_QUEUE_WIDTH_FULL - 1)
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(USER_INTERACTIVE, DISPATCH_PRIORITY_FLAG_OVERCOMMIT,
.dq_label = "com.apple.root.user-interactive-qos.overcommit",
.dq_serialnum = 15,
),
};
// 串行队列源码
#define DISPATCH_QUEUE_SERIAL NULL
// 并发队列源码
#define DISPATCH_QUEUE_CONCURRENT DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, _dispatch_queue_attr_concurrent)
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) {
return _dispatch_lane_create_with_target(label, attr, DISPATCH_TARGET_QUEUE_DEFAULT, true);
}
#if OS_OBJECT_USE_OBJC
#define DISPATCH_GLOBAL_OBJECT(type, object) ((OS_OBJECT_BRIDGE type)&(object))
#define DISPATCH_QUEUE_WIDTH_MAX (DISPATCH_QUEUE_WIDTH_FULL - 2)
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);
...省略部分 - 根据dqai规范化参数...
dispatch_lane_t dq = _dispatch_object_alloc(vtable, sizeof(struct dispatch_lane_s));
// 初始化队列 并发队列 DISPATCH_QUEUE_WIDTH_MAX 串行队列 1
_dispatch_queue_init(dq, dqf, dqai.dqai_concurrent ? DISPATCH_QUEUE_WIDTH_MAX : 1, DISPATCH_QUEUE_ROLE_INNER | (dqai.dqai_inactive ? DISPATCH_QUEUE_INACTIVE : 0));
...省略部分...
return _dispatch_trace_queue_create(dq)._dq;
}
_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) { // 并发队列
dqai.dqai_concurrent = true;
return dqai;
}
#endif
...一系列操作...
return dqai;
}
static inline dispatch_queue_class_t _dispatch_queue_init(dispatch_queue_class_t dqu, dispatch_queue_flags_t dqf, uint16_t width, uint64_t initial_state_bits) {
uint64_t dq_state = DISPATCH_QUEUE_STATE_INIT_VALUE(width);
dispatch_queue_t dq = dqu._dq;
dispatch_assert((initial_state_bits & ~(DISPATCH_QUEUE_ROLE_MASK |
DISPATCH_QUEUE_INACTIVE)) == 0);
if (initial_state_bits & DISPATCH_QUEUE_INACTIVE) {
dq->do_ref_cnt += 2; // rdar://8181908 see _dispatch_lane_resume
if (dx_metatype(dq) == _DISPATCH_SOURCE_TYPE) {
dq->do_ref_cnt++; // released when DSF_DELETED is set
}
}
dq_state |= initial_state_bits;
dq->do_next = DISPATCH_OBJECT_LISTLESS;
dqf |= DQF_WIDTH(width); // 串行队列DQF_WIDTH(1) -- 主队列 -- 串行队列
os_atomic_store2o(dq, dq_atomic_flags, dqf, relaxed);
dq->dq_state = dq_state;
dq->dq_serialnum =
os_atomic_inc_orig(&_dispatch_queue_serial_numbers, relaxed);
return dqu;
}
我们可以看到初始化的时候,主队列及串行队列初始化队列都是DQF_WIDTH(1),全局并发队列DQF_WIDTH(0x1000ull - 1 = 15),并发队列DQF_WIDTH(0x1000ull - 2 = 14);
主队列的number = 1,全局队列number = 4-15,其他number也可以在Dispatch Source/init.c文件里查找到。
串行队列就类似单行道,并发队列相当于多车道,虽然都是FIFO的数据结构,但是串行队列只能往一个队列中添加任务,一定会按照放入队列的顺序进行顺序执行;
并发队列可以往多个队列中添加任务,等待线程执行队列中的任务,线程的调度队列的情况和任务的复杂度决定了任务的执行顺序。
以上就是iOS开发探索多线程GCD队列示例详解的详细内容,更多关于iOS开发多线程GCD队列的资料请关注编程网其它相关文章!
--结束END--
本文标题: iOS开发探索多线程GCD队列示例详解
本文链接: https://www.lsjlt.com/news/165230.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-01-21
2023-10-28
2023-10-28
2023-10-27
2023-10-27
2023-10-27
2023-10-27
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0