并发编程 - GCD的栅栏(dispatch_barrier_async)

news2024/9/20 13:28:05

引言

Grand Central Dispath(GCD)是苹果提供的强大工具,它几乎涵盖了多线程编程的所有方面。通过GCD,我们可以轻松地创建队列、管理线程,并以更优雅的方式处理并发任务。在前面的博客中,我们已经深入探讨了GCD的基本概念和常用方法,比如如何创建队列和管理线程。本篇博客我们将更进一步,探索GCD的一个高级功能——栅栏操作(Barrier),并了解它如何帮助我们在复杂的并发场景中实现更精细的任务控制。

栅栏操作的概念

GCD栅栏操作是一种在并发队列中控制任务执行顺序的机制。通过栅栏,我们可以确保在栅栏前提交的任务全部完成后,才会执行栅栏操作本身,且栅栏操作执行完毕后,才会继续执行栅栏后提交的任务。这样可以在并发环境中实现顺序控制,特别适用于某些任务之间需要严格前后关系的场景。

栅栏操作又分为两种,同步栅栏和异步栅栏。

同步栅栏(dispatch_barrier_sync)

同步栅栏是阻塞的,这意味着调用dispatch_barrier_sync的线程会一直等待,直到栅栏前的所有任务和栅栏操作本身都完成后,才能继续执行。

同步栅栏适用于需要立即确保栅栏操作完成后并获取结果的场景,比如栅栏操作中执行一个关键的计算,之后立即使用计算结果。

但是需要注意不要在主线程上使用dispatch_barrier_sync,因为它会阻塞当前线程,可能导致死锁或页面卡顿。

异步栅栏(dispatch_barrier_async)

异步栅栏不会阻塞调用它的现成。它会立即返回,并将栅栏操作放入队列中等待执行。栅栏操作会在栅栏前的任务完成后执行,但调用线程不会等待它的完成。

异步栅栏适用于需要确保栅栏前后的任务顺序,但不需要立即等待栅栏操作完成的场景。比如我们可以在栅栏操作中执行一些后台任务,而不需要阻塞主线程的操作。它可以有效地利用并发性能,而不影响调用线程的执行流。

栅栏的实现方式

下面我们就来看一下同步和异步栅栏的实现方式。

同步栅栏(dispatch_barrier_sync)

同步栅栏代码如下:

    //MARK: 同步栅栏
    func syncBarrier() {
        let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)

        concurrentQueue.async {
            print("Task 1")
            sleep(1)
        }

        concurrentQueue.async {
            print("Task 2")
            sleep(1)
        }

        // 同步栅栏操作
        concurrentQueue.sync(flags: .barrier) {
            print("Barrier Task")
            sleep(1)
        }

        print("Task 3 Task 4提交到队列")
        
        concurrentQueue.async {
            print("Task 3")
            sleep(1)
        }

        concurrentQueue.async {
            print("Task 4")
            sleep(1)
        }

    }
    

执行结果如下:

Task 1

Task 2

Barrier Task

Task 3 Task 4提交到队列

Task 3

Task 4

可以得出结论:

  1. 在执行Barrier Task之前,Task 1和Task 2会先被执行
  2. Barrier Task会在Task 1和Task 2后完成执行,并且在Barrier Task完成之前,Task 3和Task 4不会开始执行。
  3. 由于是同步栅栏,sync(flags: .barrier)会阻塞调用线程,直到Barrier Task执行完成。

异步栅栏(dispatch_barrier_async)

异步栅栏代码如下:

    // MARK: 异步栅栏
    func asyncBarrier() {
        let concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)

        concurrentQueue.async {
            print("Task 1")
            sleep(1)
        }

        concurrentQueue.async {
            print("Task 2")
            sleep(1)
        }

        // 异步栅栏操作
        concurrentQueue.async(flags: .barrier) {
            print("Barrier Task")
            sleep(1)
        }
        print("Task 3 Task 4提交到队列")
        concurrentQueue.async {
            print("Task 3")
            sleep(1)
        }

        concurrentQueue.async {
            print("Task 4")
            sleep(1)
        }
    }

执行结果如下:

Task 3 Task 4提交到队列

Task 1

Task 2

Barrier Task

Task 3

Task 4

可以得出结论:

  1. Barrier Task仍然会在Task 1和Task 2之后执行,并确保在它执行完成之前不会有其他任务执行。
  2. 不同于同步栅栏,异步栅栏不会阻塞调用线程,所以代码会继续执行,任务会被正常提交到队列中。

栅栏的实践示例

虽然栅栏操作不像创建队列和管理线程那样广泛使用,但在某些特定的场景中,它仍然是不可或缺的工具。

数据整合

例如,在网络编程中,我们所需要的数据往往来自不同的服务器或者API。为了确保数据的一致性,我们可呢个需要从多个API获取数据,并在所有数据都处理完毕后,再将其进行整合、转换,最终进行本地缓存和UI更新。在这种情况下,栅栏操作可以确保这些步骤按顺序执行,从而避免数据不完整或者错误的情况发生。

示例代码如下:

    //MARK: 使用栅栏 数据整合
    func barrierOperation() {
        let concurrentQueue = DispatchQueue(label: "com.example.fileQueue", attributes: .concurrent)

        // 第一组任务:请求数据1
        concurrentQueue.async {
            print("request data 1")
            // 模拟数据请求
            sleep(1)
        }
        // 第一组任务:请求数据2
        concurrentQueue.async {
            print("request data 2")
            // 模拟数据请求
            sleep(1)
        }
        // 第一组任务:请求数据3
        concurrentQueue.async {
            print("request data 3")
            // 模拟数据请求
            sleep(1)
        }

        // 栅栏任务:处理读取的数据
        concurrentQueue.async(flags: .barrier) {
            print("Process and analyze data from both files")
        }

        // 第二组任务:将处理后的数据写回文件
        concurrentQueue.async {
            print("Write processed data to file")
        }
        // 第三组任务:其它操作
        concurrentQueue.async {
            print("Other operation")
        }

    }

读写锁

另一个典型的应用场景就是本地数据的存储和读取。为了提高程序的性能,通常我们只需要对写操作进行加锁,而读操作则可以并发进行。这是,栅栏操作可以帮助我们实现高效的读写锁,确保数据在多线程环境下的安全和一致性。

class ReadWriteLock: NSObject {
    
    /// 数据
    private var dataDict = [String: String]()
    /// 并发队列
    private var concurrentQueue = DispatchQueue(label: "com.example.concurrentQueue", attributes: .concurrent)
    
    //MARK: 异步 读
    func readData(key: String) -> String? {
        var result: String?
        concurrentQueue.sync {
            result = self.dataDict[key]
        }
        return result
    }
    
    
    //MARK: 写
    func writeData(key: String, value: String) {
        concurrentQueue.async(flags: .barrier) {
            self.dataDict[key] = value
        }
    }
}

结语

在本文中,我们深入探讨了GCD栅栏操作的高级用法,通过实际示例展示了如何在并发队列中执行任务的顺序控制和实现读写锁。栅栏操作是处理复杂并发任务时非常强大的工具,它能够确保任务的顺序执行,避免数据不一致或冲突。

然而,在使用栅栏操作时,需要特别注意一些关键点。首先,尽量避免在全局并发队列中使用栅栏操作,因为全局并发队列通常用于系统级任务。栅栏操作的使用应局限于专门为特定任务创建的并发队列,这样可以更好地控制执行任务的顺序。

总之,理解并正确应用GCD栅栏可以帮助我们更有效地管理并发任务,提升应用的稳定性和性能。希望本篇博客能够为你的开发工作提供有价值的参考。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SpringBoot+Vue+MySQL的校园健康驿站管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 本文设计并实现了一个基于SpringBoot后端、Vue前端与MySQL数据库的校园健康驿站管理系统。该系统旨在通过数字化手段,全面管理学生的健康信息,包括体温监测、疫苗接种记录、健康状况申报等,为…

【Canvas与表盘】绘制黄蓝两色简约表盘

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>黄蓝卡通手表</title><style type"text/css">…

【我的Android进阶之旅】解决CardView四个圆角有白边的问题

文章目录 一、问题描述二、分析CardView出现白边的原因三、如何解决这个问题?3.1 如何修复?3.2 为什么这样可以修复?3.3 示例代码3.4 总结一、问题描述 在实现一个RecycleView的Item时候,样式需要用到卡片式效果,于是想到用CardView来实现,但是最终发现运行出来的效果,…

(微服务项目)新闻头条——Day1

最近发生了很多事情&#xff0c;躺了一阵子&#xff0c;也是终于振作起来做自己的事情了.... 有的人追求精彩而活&#xff0c;而即使瘦若浮游&#xff0c;仍旧痴迷71种滋味&#xff0c;而有的人寿命明却装醉不得自由虚度自己的光阴&#xff0c;年华终究在最后一刻幡然醒悟&…

Android Studio新建工程(Java语言环境)

一、新建工程流程(java语言环境) 1、File->New->New Project 2、选择“Empty Views Activity” -> Next 3、创建项目名称/项目路径/语言环境 1&#xff09;项目名称&#xff1a;使用默认Name 或 修改Name 2) Package name&#xff1a;每个项目的这个名称唯一&…

MySQL 创建数据库和表全攻略

一、MySQL 创建数据库与表的重要性 MySQL 作为广泛应用的关系型数据库管理系统&#xff0c;创建数据库和表具有至关重要的意义。 在数据存储方面&#xff0c;数据库就如同一个巨大的仓库&#xff0c;为各类数据提供了安全、有序的存储环境。通过创建数据库&#xff0c;可以将相…

计算机毕业设计体育资讯个性化推荐网站网站内容留言评论前台注册后台管理/springboot/javaWEB/J2EE/MYSQL数据库/vue前后分离小程序

一、网站内容 ‌个性化推荐‌&#xff1a;根据用户浏览历史和兴趣&#xff0c;推送相关体育资讯。‌丰富资讯‌&#xff1a;包含体育赛事直播、新闻报道、专栏评论等。 二、留言评论系统 ‌用户互动‌&#xff1a;允许用户对资讯进行留言和评论&#xff0c;增强社区互动性。…

python基础知识 (五)--容器、索引、切片、字符串的遍历、查找、修改元素

目录 容器 容器大总结 索引 切片 字符串的遍历 for循环语法&#xff1a; while循环语法&#xff1a; 查找元素 修改元素 例题 1.验证码 2.抽取大红包 3.a和b互换位置 容器 在Python中&#xff0c;常见容器有&#xff1a; &#xff08;1&#xff09;字符串&#x…

k8s的加密配置secret和应用配置configmap

目录 加密配置 secret的三种类型 创建opaque类型的两种方式 方法一 方法二 如何把secret挂载到pod当中 把secret作为环境变量传到pod当中 指定harbor私有仓库加密的secret配置 应用配置 configmap 创建configmap的方式 在pod里面用configmap做pod的环境变量 **用c…

[Linux入门]---使用exec函数实现简易shell

文章目录 1.简易实现2.人机交互&#xff0c;获取命令行3.命令行分割4.执行命令5.内建命令6.myshell代码 1.简易实现 2.人机交互&#xff0c;获取命令行 代码如下&#xff1a; int quit0; #define LEFT "[" #define RIGHT "]" #define LABLE "#&quo…

如何免费试用OpenAI o1 preview大模型

OpenAI于 2024 年 9 月 12 日推出 o1&#xff08;以前称为 Strawberry 项目&#xff09; 。这一系列新推理模型旨在更有效地解决复杂问题。ChatGPT Plus 和 Team 用户可以访问 o1-preview 和 o1-mini&#xff0c;但消息量有限。 OpenAI o1-preview与 GPT-o对比 o1-previewGPT…

Excel 基础知识-操作手册2

十、查找与引用函数 Excel中的查找与引用函数非常丰富&#xff0c;以下是一些主要的函数及其使用示例&#xff1a; 1. **VLOOKUP** - 语法&#xff1a;VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup]) - 示例&#xff1a;假设A列是员工编号&#xff0c;B…

27 顺序表 · 链表

目录 一、单链表 &#xff08;一&#xff09;概念 1、节点 2、链表的性质 &#xff08;二&#xff09;单链表的实现 &#xff08;三&#xff09;单链表算法题 1、移除链表元素 2、反转链表 3、链表的中间节点 4、合并两个有序的单链表 5、链表分割 6、链表的回文结构…

pdf怎么加页码?5种pdf添加页码指南分享,快来领取!

如何在一个包含大量页面的大型pdf文件中快速找到特定的页面或信息呢&#xff1f;最简便的方法就是为pdf添加页码。pdf添加页码能够清晰显示页面顺序&#xff0c;帮助读者轻松浏览大型pdf文档&#xff0c;同时也便于寻找特定章节和确定整体长度。然而&#xff0c;并非所有pdf文件…

VirtualBox Install MacOS

环境搭建 git clone https://github.com/myspaghetti/macos-virtualbox 脚本配置 修改macos-guest-virtualbox.sh部分内容为 vm_name"macOS" # name of the VirtualBox virtual machine macOS_release_name"Catalina" # install &quo…

PHP 环境搭建教程

搭建一个稳定的PHP开发环境是开发Web应用的基础。在Linux系统上&#xff0c;LAMP&#xff08;Linux, Apache, MySQL/MariaDB, PHP&#xff09;堆栈是最广泛使用的组合。本文将详细介绍如何在Linux上搭建PHP开发环境&#xff0c;涵盖安装步骤、配置和测试。更多内容&#xff0c;…

Docker操作MySQL

1&#xff0c;拷贝&#xff1b; docker cp mysql01:/etc/mysql .2&#xff0c;修改conf.d和mysql.conf.d文件 3&#xff0c; vim mysql/my.cnf 4&#xff0c;拷贝并替换my.cnf文件 5&#xff0c;mysql镜像重启命令&#xff1a; docker exec -it mysql01 -uroot -p0000006&…

LOAM学习

LOAM Ceres Solver 中的LocalParameterization理解ALOAM雷达里程计主要步骤论文A-LOAM laser Odometry代码LiDAR Odometry寻找角点特征代码流程分析寻找面点特征 求解器设置 Ceres Solver 中的LocalParameterization理解 该LocalParameterization类用来解决非线性优化中的过参…

最全的软件测试面试题(含答案)

软件的生命周期(prdctrm) 计划阶段(planning)-〉需求分析(requirement)-〉设计阶段(design)-〉编码(coding)->测试(testing)->运行与维护(running maintrnacne) 测试用例 用例编号  测试项目  测试标题  重要级别  预置条件  输入数据  执行步骤   预期结果 1…

python做游戏好用吗

Python做游戏是完全可以的&#xff0c;而且也非常简单&#xff0c;有一个专门针对游戏开发的平台&#xff08;模块&#xff09;—pygame&#xff0c;允许开发人员快速设计游戏而又摆脱了低级语言的束缚&#xff0c;下面我简单介绍一下这个模块的安装和使用&#xff1a; 1、首先…