iOS与OS多线程和内存管理---GCD的API详解

2024-06-17 02:48

本文主要是介绍iOS与OS多线程和内存管理---GCD的API详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文是学习《Objective-C高级编程:iOS与OS多线程和内存管理》的总结,详细的介绍了dispatch_queue_creat、Main Dispatch Queue/Global Dispatch Queue、dispatch_set_target_queue、dispatch_after、Dispatch Group、dispatch_apply、dispatch–sync(async)、 dispatch_suspend/dispatch_resume以及Dispatch Semaphore,并且编写了例子练习并验证。
Demo:https://github.com/onebutterflyW/GCD.git
3.2.2 dispatch_queue_create—第一种获取Dispatch Queue方法

使用dispatch_queue_create可创建Dispatch Queue,但有以下说明。
(1)关于Serial Dispatch Queue创建个数的问题,我们知道Concurrent Dispatch Queue可并行执行多个追加处理,Serial Dispatch Queue只能执行1个追加处理,但是使用dispatch_queue_create可以创键多个Serial Dispatch Queue,此时各个Serial Dispatch Queue将比性处理,因为每个Serial Dispatch Queue生成并追加处理,系统就会生成一条线程。这样的话多个Serial Dispatch Queue就是并行处理。所以Serial Dispatch Queue的生成量应该仅限于所必须的数量。
影响,过多的使用多线程,会大量消耗内存,引起大量的上下文切换,降低系统性能
使用Serial Dispatch Queue;在多线程更新相同的资源导致数据竞争时使用Serial Dispatch Queue
(2)该函数第一个参数指定生成线程的名字,退浆使用app ID的逆序;第二个参数当生成Serial Dipatch Queue时设置为NULL,当生成Concurrent Dispatch Queue时设置为Dispatch_QUEUE_CONCURRENT
(3)生成的Dispatch Queue必须由程序员释放,因为它没有像block那样作为OC对象来处理,所以使用完生成的对象,要使用dispatch_release来释放,使用dispatch_reatain持有,在GCD的几个API中含有create的函数在不需要生成对象的时候要使用release函数释放,但是实际上在使用ARC后,使用dispatch_release后用error。这一点不清楚,书上说的在什么情况下使用

3.2.3 Main Dispatch Queue/Global Dispatch Queue—第二种获取Dispatch Queue方法
(1)Main Dispatch Queue是在主线程中执行的Dispatch Queue,所以是Serial Dispatch Queue,追加到Main Dispatch Queue中的处理会在主线程的RunLoop中指向,所以可以将更新用户界面的代码加入到Main Dispatch Queue中,这与NSObject类得 performSelectorOnMainThread实例方法相同。
(2)Global Dispatch Queue是所有应用程序都可以使用的Concurrent Dispatch Queue,Global Dispatch Queue有四个优先级,分别是High Priority、Default priority、Low priority以及background priority,使用优先级是XNU的内核处理的,并不能保证实时性,只是大致判断优先级;在设置了优先级后,XNU内核管理的用于Global Dispatch Queue的线程,将按照设置的优先级作为线程执行的优先级。

  //获取主线程dispatch_queue_t mainQueue = dispatch_get_main_queue();//获取各个优先级的Global Dispatch Queuedispatch_queue_t highGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);dispatch_queue_t defaultGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_queue_t lowGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);dispatch_queue_t backGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

3.2.4 dispatch_set_target_queue
(1)使用dispatch_set_target_queue更改Dispatch Queue的执行优先级
dispatch_queue_create函数生成的Dispatch Queue不管是Serial Dispatch Queue还是Concurrent Dispatch Queue,执行的优先级都与默认优先级的Global Dispatch queue相同,如果需要变更生成的Dispatch Queue的执行优先级则需要使用dispatch_set_target_queue函数

 - (void)testTeagerQueue1 {dispatch_queue_t serialQueue = dispatch_queue_create("com.GCD.www",NULL);dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0);dispatch_set_target_queue(serialQueue, globalQueue);// 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。}

(2)使用dispatch_set_target_queue修改用户队列的目标队列,使多个serial queue在目标queue上一次只有一个执行

    1   -(void)testTargetQueue {  2       dispatch_queue_t targetQueue = dispatch_queue_create("com.GCD.www", DISPATCH_QUEUE_SERIAL);  3         4         5         6       dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);  7       dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);  8       dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);  9         10      dispatch_set_target_queue(queue1, targetQueue);  11      dispatch_set_target_queue(queue2, targetQueue);  12      dispatch_set_target_queue(queue3, targetQueue);  13        14        15      dispatch_async(queue1, ^{  16          NSLog(@"1 in");  17          [NSThread sleepForTimeInterval:3.f];  18          NSLog(@"1 out");  19      });  20    21      dispatch_async(queue2, ^{  22          NSLog(@"2 in");  23          [NSThread sleepForTimeInterval:2.f];  24          NSLog(@"2 out");  25      });  26      dispatch_async(queue3, ^{  27          NSLog(@"3 in");  28          [NSThread sleepForTimeInterval:1.f];  29          NSLog(@"3 out");  30      });  31        32        33        34  }  

3.2.5 dispatch_after
使用场景:想在指定的时间后执行某些处理的情况,可使用dispatch_after
下面是在3秒后将指定的Block追加到Main Dispatch Queue中的代码
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull*NSEC_PER_SEC);

    dispatch_after(time, dispatch_get_main_queue(), ^{NSLog(@"使用dispatch_after,3秒后追加操作");});

注意:dispatch_after不是在3秒后执行block追加的处理,而是在指定的时间后追加处理。此代码和3秒后使用dispatch_async函数追加到Main Dispatch Queue相同

3.2.6 Dispatch Group
(1)使用场景,有一个需求:在追加到Dispatch Queue中的的多个处理全部处理完毕后,想执行结束处理。如果是Serial Dispatch Queue时比较简单,将多个处理追加到同一个Serial Dispatch Queue中,在最后追加结束处理即可,但如果使用Concurrent Dispatch Queue时,因为多个处理是并行执行的就不好处理了,此时可以使用Dispatch Group实现。

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, queue, ^{NSLog(@"blk0");});dispatch_group_async(group, queue, ^{NSLog(@"blk1");});dispatch_group_async(group, queue, ^{NSLog(@"blk2");});dispatch_group_notify(group, queue, ^{NSLog(@"done");});

输出
2017-03-02 10:32:13.715 GCD[872:400958] blk1
2017-03-02 10:32:13.715 GCD[872:400960] blk2
2017-03-02 10:32:13.715 GCD[872:400959] blk0
2017-03-02 10:32:13.715 GCD[872:400959] done

总结:无论是向Serial Dispatch Queue或者Concurrent Dispatch Queue追加多个处理,都可使用Dispatch Group实现多个追加处理执行完毕,再追加结束处理到Dispatch Queue中。

(2)书上说明,使用完毕Dispatch Group后使用dispatch_release释放,但实际上在ARC下添加它出错。

(3)dispatch_group_wait函数,等待(这里的等待的意思是在dispatch_group_wait指定的时间或属于Dispatch Group的处理全部执行完之前,执行该函数的线程是停止的)全部处理执行完,返回值如果为0说明,追加的全部处理执行完,如果不为0,因为着虽然过了指定的时间(第二个参数指定时间),但属于Dispatch Group的某个处理还在执行。当第二个参数为DISPATCH_TIME_FOREVER时,意味着永久等待,他的返回值恒为0;如果是DISPATCH_TIME_NOW,则不用等待即可判定属于Dispatch Group的处理是否结束。检查属于Dispatch Group的处理是否结束是在主线程的Runloop的每次循环中判断的,不需要等待多余的时间

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, queue, ^{NSLog(@"blk0");});dispatch_group_async(group, queue, ^{for (int i = 0; i < 400; i ++) {NSLog(@"blk1");}});dispatch_group_async(group, queue, ^{NSLog(@"blk2");});dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull*NSEC_PER_SEC);long result  = dispatch_group_wait(group, time);if (result == 0) {NSLog(@" dispatch_group_wait==0:%@",[NSThread currentThread]);}else{NSLog(@" dispatch_group_wait!=0:%@",[NSThread currentThread]);}

3.2.7dispatch_barrier_async

官方:一个dispatch barrier 允许在一个并发队列中创建一个同步点。调用这个函数总是在barrier block被提交之后立即返回,不会等到block被执行。当在并发队列中遇到一个barrier, 他会延迟执行barrier的block(barrier block已经到并发队列的最前端,他不会立即执行),会等待所有在barrier之前提交的blocks执行结束,barrier block才开始执行。 所有在barrier block之后提交的blocks会等到barrier block结束之后才执行。
使用dispatch_barrier_async和Concurrent Dispatch Queue一起使用可实现高效率的数据库访问和文件访问。写入处理不可与其他写入处理或读取处理并行执行,但是读取处理与其他读取处理并行执行不会发生问题。即,为了高效访问,将读取处理追加到Concurrent Dispatch Queue中,写入处理在任一个读取处理没有执行的状态下,追加到Serial Dispatch Queue中即可。

    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.GCD.www", DISPATCH_QUEUE_CONCURRENT);dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-1");});dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-2");});dispatch_barrier_async(concurrentQueue, ^(){NSLog(@"dispatch-barrier");});dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-3");});dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-4");});dispatch_async(concurrentQueue, ^(){NSLog(@"dispatch-5");});

输出
2017-03-02 11:10:44.948 GCD[979:505579] dispatch-2
2017-03-02 11:10:44.948 GCD[979:505580] dispatch-1
2017-03-02 11:10:44.948 GCD[979:505580] dispatch-barrier
2017-03-02 11:10:44.948 GCD[979:505580] dispatch-3
2017-03-02 11:10:44.948 GCD[979:505579] dispatch-4
2017-03-02 11:10:44.948 GCD[979:505581] dispatch-5

3.2.8 dispatch_sync

dispatch_sync是同步添加操作,它是等待添加进队列里的操作完成之后再继续执行
dispatch_async是异步添加操作,它不做任何等待,将操作添加到队列中立即返回继续执行
队列有四种:自己创建的Serial Dispatch queue、Concurrent Dispatch Queue、Main Dispatch Queue以及Global Dispatch queue;所以就会有八种组合
1、dispatch_sync不会创建新线程,添加的任务在主线程中串行执行
(1)dispatch_sync与自己创建的Serial Dispatch queue,不会创键新的线程,并且添加的任务在主线程中串行执行
(2)dispatch_sync自己创建的Concurrent Dispatch Queue,不会创键新的线程,并且添加的任务在主线程中串行执行
(3)dispatch_sync与Main Dispatch Queue会产生死锁,
(4)dispatch_sync与Global Dispatch queue不会创键新的线程,并且添加的任务在主线程中串行执行

2、dispatch_async
(1)dispatch_async与自己创建的Serial Dispatch queue,dispatch_async添加任务说明有开启新线程的能力,Serial Dispatch queue,说明XXNU内核会开启一个线程,但添加的任务串行执行
(2)dispatch_async与自己创建的Concurrent Dispatch queue,dispatch_async添加任务说明有开启新线程的能力,Concurrent Dispatch queue队列说明XXNU内核会创建多个新的线程务并行执行添加的任务
(3)dispatch_async与Main Dispatch Queue,因为任务在主队列中,所以不会创建新线程,任务会主线程中串行执行
(4)dispatch_async与Global Dispatch queue,dispatch_async添加任务说明有开启新线程的能力;使用Global Dispatch queue队列,XNU内核创建多个新的线程,添加的任务并行执行

Demo:

3.2.9 dispatch_apply

dispatch_apply:按指定的次数将指定的block追加到指定的Dispatch Queue中,并等待全部处理执行结束;相当于Dispatch Group和Dispatch_sync的关联API。(这里说与Dispatch_sync有关是因为dispatch_apply函数会等待添加的任务执行结束,效果上相当于Dispatch_sync;所以推荐在Dispatch_async中非同步的使用dispatch_apply);其实dispatch_apply可以使用Dispatch Group中的dispatch_group_notify来实现.

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_apply(10, queue, ^(size_t index) {NSLog(@"%zu",index);});NSLog(@“done");

输出:
2017-03-02 13:30:36.959 GCD[1123:836861] 0
2017-03-02 13:30:36.959 GCD[1123:837355] 2
2017-03-02 13:30:36.959 GCD[1123:837332] 1
2017-03-02 13:30:36.959 GCD[1123:837374] 3
2017-03-02 13:30:36.959 GCD[1123:836861] 4
2017-03-02 13:30:36.959 GCD[1123:837355] 5
2017-03-02 13:30:36.960 GCD[1123:836861] 7
2017-03-02 13:30:36.960 GCD[1123:837332] 6
2017-03-02 13:30:36.960 GCD[1123:837374] 8
2017-03-02 13:30:36.960 GCD[1123:837355] 9
2017-03-02 13:30:36.960 GCD[1123:836861] done

使用Dispatch Group中的dispatch_group_notify来实现上面代码的类似功能

   dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, queue, ^{NSLog(@"1");});dispatch_group_async(group, queue, ^{NSLog(@"2");});dispatch_group_async(group, queue, ^{NSLog(@"3");});dispatch_group_async(group, queue, ^{NSLog(@"4");});dispatch_group_async(group, queue, ^{NSLog(@"5");});dispatch_group_async(group, queue, ^{NSLog(@"6");});dispatch_group_async(group, queue, ^{NSLog(@"7");});dispatch_group_async(group, queue, ^{NSLog(@"8");});dispatch_group_async(group, queue, ^{NSLog(@"9");});dispatch_group_async(group, queue, ^{NSLog(@"10");});dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"done");});

输出
2017-03-02 13:25:53.679 GCD[1108:817573] 3
2017-03-02 13:25:53.679 GCD[1108:817580] 1
2017-03-02 13:25:53.679 GCD[1108:817586] 2
2017-03-02 13:25:53.679 GCD[1108:817608] 4
2017-03-02 13:25:53.679 GCD[1108:817609] 5
2017-03-02 13:25:53.679 GCD[1108:817573] 6
2017-03-02 13:25:53.680 GCD[1108:817580] 7
2017-03-02 13:25:53.680 GCD[1108:817586] 8
2017-03-02 13:25:53.680 GCD[1108:817608] 9
2017-03-02 13:25:53.680 GCD[1108:817609] 10
2017-03-02 13:25:53.682 GCD[1108:816657] done

3.2.10 dispatch_suspend/dispatch_resume
(1)dispatch_suspend挂起指定的线程
(2)dispatch_resume回复指定的线程
两个个函数对已经执行的处理无影响,主音箱尚未处理的操作

3.2.11 Dispatch Semaphore

信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。
  在GCD中有三个函数是semaphore的操作,分别是:
  dispatch_semaphore_create   创建一个semaphore
  dispatch_semaphore_signal   发送一个信号
  dispatch_semaphore_wait    等待信号
  简单的介绍一下这三个函数,第一个函数有一个整形的参数,我们可以理解为信号的总量,dispatch_semaphore_signal是发送一个信号,自然会让信号总量加1,dispatch_semaphore_wait等待信号,可以正常的执行,并让信号总量-1,当信号总量少于0的时候就会一直等待。根据这样的原理,我们便可以快速的创建一个并发控制来同步任务和有限资源访问控制。

    1   dispatch_group_t group = dispatch_group_create();   2       dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);   3       dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);   4       for (int i = 0; i < 100; i++)   5       {   6           dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);   7           dispatch_group_async(group, queue, ^{   8               NSLog(@"%i",i);   9               sleep(2);   10              dispatch_semaphore_signal(semaphore);   11          });   12      }   13      dispatch_group_wait(group, DISPATCH_TIME_FOREVER);   14      dispatch_release(group);   15      dispatch_release(semaphore);  

 简单的介绍一下这一段代码,创建了一个初使值为10的semaphore,每一次for循环都会创建一个新的线程,线程结束的时候会发送一个信号,线程创建之前会信号等待,所以当同时创建了10个线程之后,for循环就会阻塞,等待有线程结束之后会增加一个信号才继续执行,如此就形成了对并发的控制,如上就是一个并发数为10的一个线程队列。

3.2.12 dispatch_once
dispatch_once保证指定的代码只执行一次,多用于单例模式
使用模式

static dispatch_once_t pres;dispatch_once(&prers,^{/*
初始化
*/});

这篇关于iOS与OS多线程和内存管理---GCD的API详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1068281

相关文章

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

K8S(Kubernetes)开源的容器编排平台安装步骤详解

K8S(Kubernetes)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。以下是K8S容器编排平台的安装步骤、使用方式及特点的概述: 安装步骤: 安装Docker:K8S需要基于Docker来运行容器化应用程序。首先要在所有节点上安装Docker引擎。 安装Kubernetes Master:在集群中选择一台主机作为Master节点,安装K8S的控制平面组件,如AP

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动