使用 GCD 实现属性的多读单写

news2024/11/23 19:44:50

在这里插入图片描述

使用 Grand Central Dispatch (GCD) 实现多读单写的属性

  1. 首先需要确保在多线程环境下的线程安全性。
  2. 可以使用 GCD 提供的读写锁机制 dispatch_rwlock_t 或者 dispatch_queue_t 来实现这个功能。

Swift版本的实现

  1. 怎样创建一个并发队列 ?
    // 使用 Swift 来实现的首个好处就是:避免使用低等级的 C 语言 API (真的很难用🤣)
    let queue = DispatchQueue(label: "io.sqi.queue.concurrent", attributes: .concurrent)
    
  2. Swift 的属性怎样重写 setter 和 getter ?(是不是 Objective-C 喝多了😂, 应该像下面 3 这样问)
  3. 应该使用什么类型的属性,setter 和 getter 怎样实现 ?

    使用计算属性,setter 使用 set { }, 注意不是 didSet { }, getter 使用 get { }

import Foundation

class SQIObject<T> {
    private var _threadSafeProperty: T
    private let queue = DispatchQueue(label: "io.sqi.threadSafeProperty", attributes: .concurrent)
    
    init(threadSafeProperty: T) {
        self._threadSafeProperty = threadSafeProperty
    }
    
    var threadSafeProperty: T {
        get {
            return queue.sync {
                return _threadSafeProperty
            }
        }
        set {
            queue.async(flags: .barrier) {
            	// 如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 newValue
                self._threadSafeProperty = newValue
            }
        }
    }
}

// 示例使用
let demo = SQIObject(threadSafeProperty: 0)

// 多读示例
DispatchQueue.concurrentPerform(iterations: 10) { index in
    print("Read \(index): \(demo.threadSafeProperty)")
}

// 单写示例
DispatchQueue.global().async {
    demo.threadSafeProperty = 42
    print("ThreadSafeProperty updated to 42")
}

// 确保程序不会立即退出
DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
    print("Final threadSafeProperty: \(demo.threadSafeProperty)")
}
RunLoop.main.run(until: Date(timeIntervalSinceNow: 2))

在这个示例中:

  1. SQIObject 类封装了一个泛型属性,并使用 GCD 的并发队列来确保线程安全。
  2. 读取操作使用 queue.sync 同步读取,以确保多个读取操作可以同时进行。
  3. 写入操作使用 queue.async(flags: .barrier),这确保了在写入操作执行时,所有的读取操作都会被阻塞,直到写入操作完成。这就实现了多读单写的属性。

Objective-C 版本的实现

利用 Grand Central Dispatch (GCD) 中的并发队列和屏障块来确保线程安全。

#import <Foundation/Foundation.h>

@interface SQIObject : NSObject
// 一个线程安全的多读单写属性
@property (nonatomic, strong) id threadSafeProperty;

- (instancetype)initWithThreadSafeProperty:(id)threadSafeProperty;

@end

@implementation SQIObject {
    id _threadSafeProperty;
    dispatch_queue_t _queue;
}

- (instancetype)initWithThreadSafeProperty:(id)threadSafeProperty {
    self = [super init];
    if (self) {
        _threadSafeProperty = threadSafeProperty;
        _queue = dispatch_queue_create("io.sqi.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
    }
    return self;
}

- (id)threadSafeProperty {
    __block id result;
    dispatch_sync(_queue, ^{
        result = _threadSafeProperty;
    });
    return result;
}

- (void)setThreadSafeProperty:(id)threadSafeProperty {
    dispatch_barrier_async(_queue, ^{
        _threadSafeProperty = threadSafeProperty;
    });
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SQIObject *object = [[SQIObject alloc] initWithThreadSafeProperty:@0];
        
        // 多读示例
        dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
            NSLog(@"Read %zu: %@", index, [object threadSafeProperty]);
        });
        
        // 单写示例
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [SQIObject setThreadSafeProperty:@42];
            NSLog(@"ThreadSafeProperty updated to 42");
        });
        
        // 确保程序不会立即退出
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"Final threadSafeProperty: %@", [object threadSafeProperty]);
            CFRunLoopStop(CFRunLoopGetMain());
        });
        
        CFRunLoopRun();
    }
    return 0;
}

在这个示例中:

  1. SQIObject 类封装了一个属性 threadSafeProperty,并使用 GCD 的并发队列 _queue 来确保线程安全。
  2. 读取操作使用 dispatch_sync 同步读取,以确保多个读取操作可以同时进行。
  3. 写入操作使用 dispatch_barrier_async,这确保了在写入操作执行时,所有的读取操作都会被阻塞,直到写入操作完成。这就实现了多读单写的属性。

细节分析

getter 方法为什么这样实现 ?感觉有点怪

- (id)threadSafeProperty {
    __block id result;
    dispatch_sync(_queue, ^{
        result = _threadSafeProperty;
    });
    return result;
}
  1. 首先,想要将读取操作放入队列,必须要有如下片段:

    - (id)threadSafeProperty {
          dispatch_(a)sync(_queue, ^{
              // 读取操作
    	  });
    }
    
    
  2. 不能在 block 内部直接 return, 会报类型不匹配

    // Incompatible block pointer types passing 'id (^)(void)' to parameter of type 'dispatch_block_t _Nonnull' (aka 'void (^)(void)')
    - (id)threadSafeProperty {
          dispatch_(a)sync(_queue, ^{
              return _threadSafeProperty;
    	  });
    }
    
  3. 所以只能先声明临时变量,然后在 block 中执行 assignment (赋值),完成后,return 出去,结果就是:

    - (id)threadSafeProperty {
        __block id result;
        dispatch_(a)sync(_queue, ^{
            result = _threadSafeProperty;
        });
        return result;
    }
    
    
  4. 而如果想要获取有效的 return 值,GCD Block 中的操作必须 block 线程,在 return 之前完成 assignment, 故只能选择 dispatch_sync,所以最终的结果是:

    - (id)threadSafeProperty {
        __block id result;
        dispatch_sync(_queue, ^{
            result = _threadSafeProperty;
        });
        return result;
    }
    
    

在这里插入图片描述

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

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

相关文章

swift使用swift-protobuf协议通讯,使用指北

什么是Protobuf Protobuf&#xff08;Protocol Buffers&#xff09;协议&#x1f609; Protobuf 是一种由 Google 开发的二进制序列化格式和相关的技术&#xff0c;它用于高效地序列化和反序列化结构化数据&#xff0c;通常用于网络通信、数据存储等场景。 为什么要使用Proto…

(四)主成分分析和因子分析法基础练习题(17道选择题)

本文整理了主成分分析和因子分析法相关的练习题&#xff0c;共17道&#xff0c;适用于想巩固理论基础的同学。来源&#xff1a;如荷学数据科学题库&#xff08;CDA二级-宏观业务分析方法&#xff09;。 1&#xff09; 2&#xff09; 3&#xff09; 4&#xff09; 5&#xff09…

5千关打乱汉字选择成语题ACCESS\EXCEL数据库

成语类的游戏之所有很火&#xff0c;最大的原因是门槛低&#xff0c;读过小学的人几乎都可以尝试过几关&#xff0c;之前发过《885成语错别字选择题库》、《638成语错别字题库》、《7千8百多条成语填空选字》、《3600关成语填字APP游戏》、《600多个看图猜成语成语图片》《315四…

HMI之王 STM32H7S7

还要什么自行车啊 感谢原厂精彩培训和慷慨赠板&#xff01; 以下列示几个关注的点&#xff0c;计划做成系列&#xff0c;随缘更新&#xff0c;尽量填。 0&#xff09;1024*600分辨率配5寸触屏&#xff1b;Type-C with USB 2.0 HS interface, dual‑role‑power 终于不用2根线…

基于STM32的智能水产养殖系统(四)

硬件原理 步进电动机 步进电动机&#xff08;Step Motor 或 Stepper Motor&#xff09;是一种将电脉冲信号转换成对应的角位移或线位移的电动机。与普通电动机不同&#xff0c;步进电动机每接收到一个脉冲信号&#xff0c;就会按设定的角度&#xff08;步距角&#xff09;转动…

【git】gitee仓库本地克隆失败可能的一种解决办法

出错点&#xff1a; 在 gitee 克隆远程仓库到 本地时&#xff0c;可能会出现以下报错情况&#xff0c;无法成功克隆 正常流程&#xff1a;&#xff08;熟悉正常克隆流程的可以直接跳到下面的【解决办法】&#xff09; 我们一般复制仓库地址是在下面红线框框的位置&#xff0c…

R语言统计分析——数据管理1

参考资料&#xff1a;R语言实战【第2版】 创建分析用数据集&#xff1a; manager<-c(1,2,3,4,5) date<-c(10/24/08,10/28/08,10/1/08,10/12/08,5/1/09) country<-c(US,US,UK,UK,UK) gender<-c(M,F,F,M,F) age<-c(32,45,25,39,99) q1<-c(5,3,3,3,2) q2<-c…

解决ERROR: Cannot uninstall ‘ipython-genutils‘.的方法

删除ipython-genutils-X-pyX.egg-info文件&#xff0c;X表示对应版本&#xff0c;问题解决。

杨辉三角的快速求法

一、杨辉三角形 杨辉三角形&#xff0c;又称贾宪三角形&#xff0c;帕斯卡三角形&#xff0c;是二项式系数在三角形中的一种几何排列&#xff0c;是二项式系数在三角形中的一种几何排列&#xff0c;古称“开方作法本源图”。 杨辉三角的历史 北宋人贾宪约1050年首先使用“贾…

搭建musetalk数字人的步骤

生成数字人的视频效果 搭建步骤 下载git代码 git clone https://github.com/TMElyralab/MuseTalk.git创建conda环境 (建议使用 python 版本 >3.10 和 cuda 版本 11.7。) conda create -n musetalk python3.10进入conda环境 conda activate musetalk下载项目依赖包 pip…

如何理解光学中的群速度和相速度。

我不太明白为什么书上要区分相速度和群速度&#xff0c;不管这个&#xff0c;我想看看这两个速度在真实周期函数上的影响是如何的。 首先计算&#xff0c;直接计算三角函数我不会&#xff0c;利用复数做&#xff0c;可以取的实部。其中&#xff0c;。。 这个公式说明了什么呢…

Neo4j 创建关系

Neo4j 创建关系 在 Noe4j 中&#xff0c;关系是我们用来连接图的两个节点的元素。 这些关系具有数据的方向、类型和形式模式。 本章教你如何 建立关系在现有节点之间创建关系使用标签和属性创建关系 建立关系 我们可以使用 CREATE 子句创建关系。 我们将在方括号[]中指定关系…

后方穿行预警系统技术规范(简化版)

后方穿行预警系统技术规范(简化版) 1 系统概述2 预警区域3 预警目标4 预警条件5 指标需求1 系统概述 RCTA后方穿行预警系统工作在驾驶员有倒车意向的时候。在倒车过程中当驾驶员视线因周围障碍物被遮挡而产生碰撞风险时,系统通过光学信号对驾驶员进行提醒。 2 预警区域 RCT…

一个关于空格的Sql Server面试题

先上题目&#xff1a; 回答下面sql 的输出结果 declare s1 varchar(10) declare s2 varchar(10) set s1a b set s2a b if s1s2 select true 答案是 true 那么上面的 s1 和 s2 是否相等的呢&#xff1f; 我们再看看下面的sql declare s1 varchar(10) declare s2 varchar…

# [0619] Task01 绪论、马尔可夫过程、动态规划

easy-rl PDF版本 笔记整理 P1 - P2 joyrl 比对 补充 P1 - P3 相关 代码 整理 最新版PDF下载 地址&#xff1a;https://github.com/datawhalechina/easy-rl/releases 国内地址(推荐国内读者使用)&#xff1a; 链接: https://pan.baidu.com/s/1isqQnpVRWbb3yh83Vs0kbw 提取码: us…

xcode和iPhone真机或者watch真机连接问题

1.如果真机是第一次连接xocde&#xff0c;就需要开启真机上的开发者模式&#xff0c;开启开发者模式的方式&#xff1a; iphone/ipad开启方式: 设置 > 隐私与安全 > 开发者模式 > 开启&#xff0c;然后重启就可以了 watch设置&#xff1a;很麻烦&#xff0c;看文章…

Hive期末总结

hive的概念&#xff0c;谁开发的 Apache Hive 是一个Apache 软件基金会维护的开源项目 Facebook贡献 hive是一个基于hadoop的数据仓库工具&#xff08;对数据汇总查询和分析的工具&#xff09; hive执行原理 Hive通过给用户提供的一系列交互接口&#xff0c;接收到用户的指令…

Leetcode3179. K 秒后第 N 个元素的值

Every day a Leetcode 题目来源&#xff1a;3179. K 秒后第 N 个元素的值 解法1&#xff1a;模拟 模拟 k 轮&#xff0c;数组保存上一次结果&#xff0c;然后计算当前轮次的结果。 代码&#xff1a; /** lc appleetcode.cn id3179 langcpp** [3179] K 秒后第 N 个元素的值…

自然语言处理概述

目录 1.概述 2.背景 3.作用 4.优缺点 4.1.优点 4.2.缺点 5.应用场景 5.1.十个应用场景 5.2.文本分类 5.2.1.一般流程 5.2.2.示例 6.使用示例 7.总结 1.概述 自然语言处理&#xff08;NLP&#xff09;是计算机科学、人工智能和语言学的交叉领域&#xff0c;旨在实…

JAVA 注解搜索工具类与注解原理讲解(获取方法和类上所有的某个注解,父类继承的注解也支持获取)

文章目录 JAVA 注解搜索工具类与注解原理讲解&#xff08;获取方法和类上所有的某个注解&#xff0c;父类继承的注解也支持获取&#xff09;代码测试方法上加注解&#xff0c;类上不加类上加注解、方法上加注解 注解原理性能测试 JAVA 注解搜索工具类与注解原理讲解&#xff08…