博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Objective-C GCD深入理解
阅读量:5104 次
发布时间:2019-06-13

本文共 5588 字,大约阅读时间需要 18 分钟。

GCD(Grand Central Dispatch),主要用于多线程编程。它屏蔽了繁琐的线程实现及管理细节,将其交由系统处理。开发者只需要定义任务block(在底层被封装成dispatch_continuation_t结构体),并提交到正确的dispatch queue中。GCD包含dispatch queue和dispatch source。

 

一、dispatch queue是FIFO队列,分为两种:

1、serial(串行),队列中的block同一个时刻只会派发给一个线程,所以要等当前block执行完,才会开始执行下一个block。

2、concurrent(并行),队列中的block同一个时刻可能会派发给多个线程,所以多个block可以同时执行。

3、苹果官方说明:

Serial queues (also known as private dispatch queues) execute one task at a time in the order in which they are added to the queue. The currently executing task runs on a distinct thread (which can vary from task to task) that is managed by the dispatch queue.

注意:串行队列中不同block可能在不同线程执行! 

 

二、使用dispatch queue的方法,也分为两种:

1、dispatch_sync(同步),调用dispatch_sync方法的线程会被阻塞,直到block执行结束。在当前线程调用dispatch_sync方法可能会导致死锁!!!

2、dispatch_async(异步),调用dispatch_async方法的线程不会被阻塞。 

NSLog(@"1"); // 死锁,只会输出1dispatch_sync(currentQueue, ^{    NSlog(@"2"); // block也提交到当前线程对应的队列        });NSlog(@"3");

死锁的理解:

1、dispatch_sync方法调用和block调用,是当前线程需要处理的两个任务。

2、dispatch_sync方法调用首先提交到队列中。

3、然后block调用提交到队尾,需要等待dispatch_sync方法调用完成。

4、而dispatch_sync方法调用,又需要等到block执行结束才能返回。这就形成了等待环,即死锁。

解决死锁:把block交由另一个线程执行

// 依次输出1、2、3NSLog(@"1"); dispatch_sync(notCurrentQueue, ^{    NSlog(@"2"); // block提交到另外一个线程对应的队列        });NSlog(@"3");   

 

三、创建队列:

dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);// label:队列的唯一标识,方便调试// attr:队列类型,NULL或者DISPATCH_QUEUE_SERIAL表示串行,DISPATCH_QUEUE_CONCURRENT表示并行dispatch_queue_t serialQueue = dispatch_queue_create("com.tencent.wechat.file", DISPATCH_QUEUE_SERIAL);dispatch_queue_t concurrentQueue = dispatch_queue_create("com.tencent.wechat.network",DISPATCH_QUEUE_CONCURRENT);  

在MRC下,dispatch_queue_create创建的队列,需要用dispatch_release释放掉。 

 

四、有时候,并不需要开发者创建队列,系统已经提供了两种很好用的队列。

主队列,对应主线程,是个串行队列:

dispatch_queue_t serialMainQueue = dispatch_get_main_queue();

全局队列,是并行队列:

dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);// identifier:队列优先级// flags:通常填0dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

  

五、dispatch_after用于延迟执行任务:

void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);// 3秒后提交block到主队列,这个时间并不精确,得看当时线程的繁忙程度dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        });

 

六、dispatch_suspend和dispatch_resume:

1、dispatch_suspend,挂起队列,只会暂停还没执行的任务,已经在执行的任务不会被影响。

2、dispatch_resume,恢复队列,继续调度还没执行的任务。

3、注意,resume一个没有被suspend的队列会导致crash!

 

七、dispatch_once

详见:

 

八、dispatch group

在多个并行block都执行完,这就可以用到dispatch group

 

 

九、dispatch_set_target_queue 

修改优先级

 

十、dispatch_barrier_async

barrier,顾名思义,添加一个障碍,就是就是个同步点。

dispatch_queue_t globalConcurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// blk1, blk2, blk3并行执行dispatch_async(globalConcurrentQueue, blk1);dispatch_async(globalConcurrentQueue, blk2);dispatch_async(globalConcurrentQueue, blk3);// blk1, blk2, blk3全部执行完,才开始执行blk4dispatch_barrier_async(globalConcurrentQueue, blk4);// blk4执行完,才开始并行执行blk5, blk6dispatch_async(globalConcurrentQueue, blk5);dispatch_async(globalConcurrentQueue, blk6);

 

十一、dispatch_apply

提交特定数量的block到队列中,并等待全部执行结束。dispatch_apply存在与dispatch_sync一样的死锁问题,因此推荐把dispatch_apply放到dispatch_async中执行。

dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_apply(10, q, ^(size_t iter) {    NSLog(@"iteration[%zu]", iter);});NSLog(@"done");输出:iteration[3]iteration[2]iteration[0]iteration[4]iteration[1]iteration[5]iteration[9]iteration[8]iteration[7]iteration[6]done // 最后输出一定是这个 

 

十二、dispatch semaphore

dispatch semaphore是信号量的封装,可用于实现线程安全,相比串行队列,粒度更小

// 创建信号量,初始值可以不为1dispatch_semaphore_t s = dispatch_semaphore_create(1);// 阻塞等待信号量大于等于1dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER);// 信号量减1,开始执行临界区代码/* 临界区代码 */// 临界区代码执行结束,信号量+1,最先等待信号量的线程得到执行dispatch_semaphore_signal(s);

 

十三、dispatch io

// 获取文件描述符dispatch_fd_t fd = open(file_path, O_RDWR);// 创建用于处理dispatch io block的队列dispatch_queue_t queue = dispatch_queue_create(queue_name, NULL);// 创建dispatch io,绑定文件描述符dispatch_io_t io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) {    // 异常    close(fd);});// 设置一次最多读取的字节数dispatch_io_set_high_water(io, size);// 这里书上说会使用global queue来并发读,但是实践没看出来,懂的同学请多多指教dispatch_io_read(io, 0, max_size, queue, ^(bool done, dispatch_data_t  _Nullable data, int error) {    // dispatch_data_t转NSString    dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) {        NSString *sData = [[NSString alloc] initWithBytes:buffer length:size encoding: NSUTF8StringEncoding];        return true;    });});

 

十四、dispatch queue VS NSThread, pthread

利用NSThread和pthread也是可以进行多线程编程的,但是

1)需要自己实现线程管理,如分配多少线程、什么时候分配、什么时候销毁等等

2)性能很难比dispatch queue好,因为dispatch queue是基于XNU内核的workqueue实现的(中间还有一层封装是Libc中的pthread_workqueue)

 

十五、dispatch source

dispatch source是内核kqueue的封装。kqueue是一种多路复用技术,用于监听内核的各种事件通知,并做出相应处理。因为利用回调代替轮询,所以kqueue的CPU占用率很小。  

// 创建sourcedispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue);// 取消source,已经在执行的handler会继续执行完,还没执行的不再执行void dispatch_source_cancel(dispatch_source_t source); // 事件source发生时,执行handlervoid dispatch_source_set_event_handler(dispatch_source_t source, dispatch_block_t handler);// 取消source时,执行handlervoid dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t handler);// 启动sourcevoid dispatch_resume(dispatch_object_t object);

 

十六、dispatch queue和block的持有关系

block持有dispatch queue

 

参考链接:

 

转载于:https://www.cnblogs.com/yangwenhuan/p/9439720.html

你可能感兴趣的文章
robot framework接口测试之一-完整的测试用例
查看>>
IOS开发:使用lipo合并armv7,i386,armv7s库文件
查看>>
使用 udev 高效、动态地管理 Linux 设备文件
查看>>
Java8函数之旅(四) --四大函数接口
查看>>
django环境处理
查看>>
记一次企业级爬虫系统升级改造(三):文本分析与数据建模规则化处理
查看>>
javascript window对象
查看>>
Android定制组件之Widget之昨天今天明天
查看>>
【JMeter】选项-函数助手对话框应用举例
查看>>
如何在Access2007中使用日期类型查询数据
查看>>
Jzoj4757 树上摩托
查看>>
CF992E Nastya and King-Shamans(线段树二分+思维)
查看>>
基于docker的spark-hadoop分布式集群之一: 环境搭建
查看>>
oracle 几个时间函数探究
查看>>
第一个Java Web程序
查看>>
Atomic
查看>>
div 显示滚动条与div显示隐藏的CSS代码
查看>>
Redis-1-安装
查看>>
Access denied for user ''@'localhost' to database 'mysql'
查看>>
微信公众号里面使用地图导航
查看>>