GCD学习

  • GCD其他方法
      • dispatch_semaphore (信号量)
      • **什么是信号量**
      • dispatch_semaphore主要作用
        • dispatch_semaphore主要作用
          • 异步转同步
          • 设置一个最大开辟的线程数
          • 加锁机制
        • dispatch_time_t 两种形式
        • GCD一次性代码(只执行一次)
      • dispatch_barrier_async/sync栅栏方法


GCD其他方法

前面对GCD进行了简单的学习,这里笔者对一些容易遗忘和重要的方法再次学习。

dispatch_semaphore (信号量)

什么是信号量

引用学长的解释

以一个停车场的运作为例。简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看 门人允许其中三辆直接进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开 车拦,放入外面的一辆进去,如果又离开两辆,则又可以放入两辆,如此往复。在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

信号量主要用作同步锁,用于控制GCD最大并发数

主要涉及以下三个函数:

// 创建信号量,value:初始信号量数 如果小于0则会返回NULL
dispatch_semaphore_create(long value); // 发送信号量是的信号量+1
dispatch_semaphore_signal(dispatch_semaphore_t deem);//可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); 

注意:信号量的使用前提是:想清楚你需要处理哪个线程等待(阻塞),又要哪个线程继续执行,然后使用信号量。

dispatch_semaphore主要作用

dispatch_semaphore主要作用
  • 保持线程同步,将异步执行任务转换为同步执行任务
  • 保证线程安全,为线程加锁
异步转同步
- (void)cjl_testSemaphore1{dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//创建异步队列dispatch_queue_t queue = dispatch_get_global_queue(0, 0);dispatch_async(queue, ^{sleep(1);NSLog(@"执行任务A");//信号量+1dispatch_semaphore_signal(semaphore);});dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);dispatch_async(queue, ^{sleep(1);NSLog(@"执行任务B");//信号量+1,相当于解锁dispatch_semaphore_signal(semaphore);});//当当前的信号量值为0时候会阻塞线,如果大于0的话,信号量-1,不阻塞线程dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);dispatch_async(queue, ^{sleep(1);NSLog(@"执行任务C");//信号量+1,相当于解锁dispatch_semaphore_signal(semaphore);});}

多次运行的结果都是A,B,C顺序执行,让A,B,C异步执行变成同步执行,dispatch_semaphore相当于加锁效果

设置一个最大开辟的线程数
- (void)cjl_testSemaphore{
//设置最大开辟的线程数为3dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);//创建一个并发队列dispatch_queue_t queue = dispatch_get_global_queue(0, 0);//开启的是10个异步的操作,通过信号量,让10个异步的最多3个m,剩下的同步等待for (NSInteger i = 0; i < 10; i++) {dispatch_async(queue, ^{//当信号量为0时候,阻塞当前线程dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);NSLog(@"执行任务 %ld", i);sleep(2);NSLog(@"完成当前任务 %ld", i);//释放信号dispatch_semaphore_signal(semaphore);});}
}

请添加图片描述

但如果sleep时间变成1,则会出现以下结果:
请添加图片描述

这是因为出现了优先级反转的问题。

先介绍什么是优先级反转。

在程序执行时多个任务可能会对同—份数据产生 竞争’因此任务会使用锁来保护共享数据°假设现在有3个任务A、B`C’它们的优先 级为A>B>C任务C在运行时持有一把锁’然后它被高优先级的任务A抢占了(任 务C的锁没有被释放)。此时任务A恰巧也想申请任务C持有的锁’但是申请失败,因 此进人阻塞状态等待任务C放锁。此时’任务B、C都处于可以运行的状态,由于任务 B的优先级高于C,因此B优先运行°综合观察该情况’就会发现任务B好像优先级高 于任务A’先于任务A执行。

请添加图片描述

信号量调度是公平队列(FIFO/不考虑 QoS)+ 系统线程调度是根据优先级(QoS)来的,两者机制不一致,所以可能造成高优先级线程因为信号量资源被低优先级线程占用而“被阻塞”。

加锁机制
  • 当你的信号设置为1的时候就相当于加锁

- (void)viewDidLoad {[super viewDidLoad];NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程NSLog(@"semaphore---begin");semaphoreLock = dispatch_semaphore_create(1);self.ticketCount = 50;// queue1 代表北京火车票售卖窗口dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);// queue2 代表上海火车票售卖窗口dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);__weak typeof(self) weakSelf = self;dispatch_async(queue1, ^{[weakSelf saleTicketSafe];});dispatch_async(queue2, ^{[weakSelf saleTicketSafe];});
}- (void)saleTicketSafe {while (1) {// 相当于加锁dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);if (self.ticketCount > 0) {  //如果还有票,继续售卖self.ticketSurplusCount--;NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);[NSThread sleepForTimeInterval:0.2];} else { //如果已卖完,关闭售票窗口NSLog(@"所有火车票均已售完"); // 相当于解锁dispatch_semaphore_signal(semaphoreLock);break;} // 相当于解锁dispatch_semaphore_signal(semaphoreLock);}    
}
dispatch_time_t 两种形式
  1. 基于dispatch_time函数,表示从当前时间开始的一个时间点
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);

使用:

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(60 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{NSLog(@"dispatch_time");
});

时间单位:

#define NSEC_PER_SEC 1000000000ull 1
#define NSEC_PER_MSEC 1000000ull 1毫秒 
#define USEC_PER_SEC 1000000ull 1
#define NSEC_PER_USEC 1000ull 1微秒
时间点:
#define DISPATCH_TIME_NOW (0ull) 0->现在
#define DISPATCH_TIME_FOREVER (~0ull) -1->永远
dispatch_time_t定义
typedef uint64_t dispatch_time_t; unsigned long long 64位无符号长整形
GCD一次性代码(只执行一次)

使用dispatch_once方法能保证某段代码在程序运行过程中只执被执行1次,即使在多线程的环境下,该方法也可以保证建成安全。之前在创建单例模式时就接触过该方法。

- (void)once {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{// 只执行 1 次的代码(这里面默认是线程安全的)});}

dispatch_barrier_async/sync栅栏方法

在访问数据库或者文件的时候,我们可以使用Serial Dispatch Queue可避免数据竞争问题。即多读单写

- (void)cjl_testBarrier{/*dispatch_barrier_sync & dispatch_barrier_async应用场景:同步锁等栅栏前追加到队列中的任务执行完毕后,再将栅栏后的任务追加到队列中。简而言之,就是先执行栅栏前任务,再执行栅栏任务,最后执行栅栏后任务- dispatch_barrier_async:前面的任务执行完毕才会来到这里- dispatch_barrier_sync:作用相同,但是这个会堵塞线程,影响后面的任务执行- dispatch_barrier_async可以控制队列中任务的执行顺序,- 而dispatch_barrier_sync不仅阻塞了队列的执行,也阻塞了线程的执行(尽量少用)*/[self cjl_testBarrier1];[self cjl_testBarrier2];
}
- (void)cjl_testBarrier1{//串行队列使用栅栏函数dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL);NSLog(@"开始 - %@", [NSThread currentThread]);dispatch_async(queue, ^{sleep(2);NSLog(@"延迟2s的任务1 - %@", [NSThread currentThread]);});NSLog(@"第一次结束 - %@", [NSThread currentThread]);//栅栏函数的作用是将队列中的任务进行分组,所以我们只要关注任务1、任务2dispatch_barrier_async(queue, ^{NSLog(@"------------栅栏任务------------%@", [NSThread currentThread]);});NSLog(@"栅栏结束 - %@", [NSThread currentThread]);dispatch_async(queue, ^{sleep(2);NSLog(@"延迟2s的任务2 - %@", [NSThread currentThread]);});NSLog(@"第二次结束 - %@", [NSThread currentThread]);
}
- (void)cjl_testBarrier2{//并发队列使用栅栏函数dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT);NSLog(@"开始 - %@", [NSThread currentThread]);dispatch_async(queue, ^{sleep(2);NSLog(@"延迟2s的任务1 - %@", [NSThread currentThread]);});NSLog(@"第一次结束 - %@", [NSThread currentThread]);//由于并发队列异步执行任务是乱序执行完毕的,所以使用栅栏函数可以很好的控制队列内任务执行的顺序dispatch_barrier_async(queue, ^{NSLog(@"------------栅栏任务------------%@", [NSThread currentThread]);});NSLog(@"栅栏结束 - %@", [NSThread currentThread]);dispatch_async(queue, ^{sleep(2);NSLog(@"延迟2s的任务2 - %@", [NSThread currentThread]);});NSLog(@"第二次结束 - %@", [NSThread currentThread]);
}

重点观察并发队列中使用栅栏函数。会在栅栏前的任务完成后,才执行栅栏后的任务。

请添加图片描述

dispatch_barrier_sync

  • 会阻塞当前线程,看打印结果可知,“start”一定在dispatch_barrier_sync函数前执行,“end”一定在dispatch_barrier_sync函数后执行
  • dispatch_barrier_sync 函数添加的block,在当前线程执行
  • 会将传入dispatch_barrier_sync函数的线程myQueue中的任务,通过dispatch_barrier_sync函数位置把前后分隔开。

dispatch_barrier_async

  • 不会阻塞当前线程,“start”,“end”与dispatch_barrier_async方法是同步执行的,执行顺序不定。
  • dispatch_barrier_sync函数添加的block,在新开的线程执行
  • 会将传入dispatch_barrier_async函数的线程myQueue中的任务,通过dispatch_barrier_async函数位置把前后分隔开。

这也是为什么栅栏结束会出现在栅栏任务之前。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90954.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90954.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/90954.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【图像处理基石】如何实现一个车辆检测算法?

基于AI的车牌检测和识别算法 问题描述、应用场景与难点 问题描述 车牌检测和识别是计算机视觉领域的一个特定任务&#xff0c;主要包含两个核心步骤&#xff1a; 车牌检测&#xff1a;从图像中准确定位车牌的位置和区域车牌识别&#xff1a;对检测到的车牌区域进行字符识别&…

计算机学报 2025年 区块链论文 录用汇总 附pdf下载

计算机学报 Year&#xff1a;2025 2024请看 1 Title: 基于区块链的动态多云多副本数据完整性审计方法研究 Authors: Key words: 区块链&#xff1b;云存储&#xff1b;多云多副本存储&#xff1b;数据完整性审计 Abstract: 随着云计算技术的快速发展和云存储服务的日益…

计算机网络-UDP协议

UDP&#xff08;用户数据报协议&#xff09;是传输层的一种无连接、不可靠、轻量级的协议&#xff0c;适用于对实时性要求高、能容忍少量数据丢失的场景&#xff08;如视频流、DNS查询等&#xff09;。以下是UDP的详细解析&#xff1a;1. UDP的核心特点特性说明无连接通信前无需…

子域名收集和c段查询

子域名收集方法一、sitesite&#xff1a; 要查询的域名可以查到相关网站二、oneforall &#xff08;子域名查找工具&#xff09;下载后解压的文件夹在当前文件夹打开终端然后运行命令 python oneforall.py --target xxxxxxxx&#xff08;这里放你要查的网址&#xff09; run最…

计网-TCP拥塞控制

TCP的拥塞控制&#xff08;Congestion Control&#xff09;是核心机制之一&#xff0c;用于动态调整发送方的数据传输速率&#xff0c;避免网络因过载而出现性能急剧下降&#xff08;如丢包、延迟激增&#xff09;。其核心思想是探测网络可用带宽&#xff0c;并在拥塞发生时主动…

依赖倒置原则 Dependency Inversion Principle - DIP

基本知识 1.依赖倒置原则&#xff08;DIP&#xff09;是面向对象设计&#xff08;OOD&#xff09;中的五个基本原则之一&#xff0c;通常被称为 SOLID 原则中的 D 2.核心思想&#xff1a; 高层模块不应该依赖低层模块&#xff0c;两者都应该依赖抽象。 (High-level modules sho…

原生input添加删除图标类似vue里面移入显示删除[jquery]

<input type"text" id"servicer-search" class"form-control" autocomplete"off" />上面是刚开始的input <div class"servicer-search-box"><input type"text" id"servicer-search" cla…

整理分享 | Photoshop 2025 (v26.5) 安装记录

导语&#xff1a; 最近整理资源时&#xff0c;发现有朋友在找新版 Photoshop。正好手边有 Photoshop 2025年7月的版本&#xff08;v26.5&#xff09;&#xff0c;就记录下来分享给大家&#xff0c;供有需要的朋友参考。关于这个版本&#xff1a;这个 Photoshop v26.5 安装包&am…

【Redis】Redis 数据存储原理和结构

一、Redis 存储结构 1.1 KV结构 Redis 本质上是一个 Key-Value&#xff08;键值对&#xff0c;KV&#xff09;数据库&#xff0c;在它丰富多样的数据结构底层&#xff0c;都基于一种统一的键值对存储结构来进行数据的管理和操作 Redis 使用一个全局的哈希表来管理所有的键值对…

【RAG优化】深度剖析OCR错误,从根源修复RAG应用的识别问题

1. 引言:OCR——RAG系统中的关键问题 当我们将一个包含扫描页面的PDF或一张报告截图扔给RAG系统时,我们期望它能“读懂”里面的内容。这个“读懂”的第一步,就是OCR。然而,OCR过程并非100%准确,它受到图像质量、文字布局、字体、语言等多种因素的影响。 一个看似微不足道…

【第六节】方法与事件处理器

方法与事件处理器 方法处理器 可以用 v-on 指令监听 DOM 事件: <div id="example"> <button v-on:click="greet">Greet</button></div>绑定一个单击事件处理器到一个方法 greet 。下面在 Vue 实例中定义这个方法 var vm=new V…

大语言模型Claude 4简介

Anthropic公司成立于2021年&#xff0c;由一群OpenAI前员工组成。他们最新发布的大语言模型(Large Language Model, LLM) Claude 4系列包括两个版本&#xff1a;Claude Opus 4和Claude Sonnet 4&#xff1a;(1).Claude Sonnet 4&#xff1a;是Claude Sonnet 3.7的升级&#xff…

国产化PDF处理控件Spire.PDF教程:Python 将 PDF 转换为 Markdown (含批量转换示例)

PDF 是数字文档管理的普遍格式&#xff0c;但其固定布局特性限制了在需要灵活编辑、更新或现代工作流集成场景下的应用。相比之下&#xff0c;Markdown&#xff08;.md&#xff09;语法轻量、易读&#xff0c;非常适合网页发布、文档编写和版本控制。 E-iceblue旗下Spire系列产…

PDF转Markdown - Python 实现方案与代码

PDF作为广泛使用的文档格式&#xff0c;转换为轻量级标记语言Markdown后&#xff0c;可无缝集成到技术文档、博客平台和版本控制系统中&#xff0c;提高内容的可编辑性和可访问性。本文将详细介绍如何使用国产Spire.PDF for Python 库将 PDF 文档转换为 Markdown 格式。 技术优…

深度解析 inaSpeechSegmenter:高效音频语音分割与检测开源工具

项目简介 inaSpeechSegmenter 是法国国家视听研究院(INA)开源的音频分割与检测工具,专为广播、播客、采访、影视等多媒体内容的自动化处理设计。它能够高效地将长音频自动分割为语音、音乐、噪声、静音等片段,并支持性别检测(男声/女声),为后续的语音识别、内容检索、转…

VirtualBox安装Ubuntu 22.04后终端无法打开的解决方案

问题现象在VirtualBox中使用"快速安装"模式安装Ubuntu 22.04后图形终端&#xff08;gnome-terminal&#xff09;无法通过图标或快捷键(CtrlAltT)启动系统其他功能正常根本原因语言环境(Locale)配置异常导致&#xff1a;快速安装模式可能跳过Locale生成步骤gnome-term…

java磁盘操作与IO流(序列化、Properties类)

目录 一、磁盘操作 1、File类&#xff1a; &#xff08;1&#xff09;创建File对象&#xff1a; &#xff08;2&#xff09;获取文件信息&#xff1a; &#xff08;3&#xff09;判断文件 &#xff08;4&#xff09;删除文件 &#xff08;5&#xff09;创建文件&#xff…

【WPF】WPF Prism 开发经验总结:菜单命令删除项时报 InvalidCastException 的问题分析与解决

WPF Prism 开发经验总结&#xff1a;菜单命令删除项时报 InvalidCastException 的问题分析与解决 在 WPF Prism 项目中使用 ContextMenu 执行删除操作时&#xff0c;遇到一个令人疑惑的问题&#xff1a;命令绑定本身没有问题&#xff0c;但点击“删除”菜单后&#xff0c;程序抛…

《WebGL打造高性能3D粒子特效系统:从0到1的技术探秘》

在游戏里,爆炸时四溅的火花、魔法释放时闪烁的光晕;在可视化项目中,数据流动时呈现的璀璨光河,这些令人惊叹的效果,背后离不开强大的技术支撑。而WebGL,作为在浏览器端实现硬件加速3D图形渲染的技术,为我们开启了构建高性能3D粒子特效系统的大门。 WebGL的渲染管线是整…

全国计算机等级考试二级题库【C语言】:程序填空题型——结构体 自制答案详解合辑

二级C语言程序填空题型简介 1、/**********found**********/紧跟的下面一行的程序设空,一般为3个空; 2、常见错误: (1) (2) 3、做题推荐步骤: (1) (2) ---------------一、结构体--------------- 2、题目要求【结构体】 程序通过定义学生结构体变量,存储了学生…