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

在这里插入图片描述

使用 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;
    }
    
    

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UE5 中的碰撞问题

文章目录 一、初始准备二、重叠和碰撞三、自定义碰撞 一、初始准备 首先我们创建一个 BP_ThirdPerson 项目&#xff0c;然后在项目中创建两个 Actor 的蓝图 Blueprint 首先是一个移动的 BP_Push&#xff0c;这里使用 time line 循环旋转 cube 的相对位置 得到效果如下 然后是…

css如何动态累计数字?

导读&#xff1a;css如何动态累计数字&#xff1f;用于章节目录的序列数生成&#xff0c;用css的计数器实现起来比 js方式更简单&#xff01; 伪元素 ::after ::before伪元素设置content 可以在元素的首部和尾部添加内容&#xff0c;我们要在元素的首部添加序列号&#xff0c…

关于read,write,open时出现的文本文件和二进制文件读写的问题(怎么写入怎么读)

1、发现问题 使用read读取文本文件&#xff0c;一般采用字符空间作为缓存&#xff0c;最后输出&#xff1b; 使用read读取二进制文件&#xff0c;这里采用整数读取的展示&#xff1a; 首先创建文本文件&#xff0c;用write写入i的值到文件中&#xff1b; 再通过lseek改变读写一…

Day9 —— 大数据技术之ZooKeeper

ZooKeeper快速入门系列 ZooKeeper的概述什么是ZooKeeper&#xff1f;ZooKeeper的特点和功能使用ZooKeeper的原因 ZooKeeper数据模型ZooKeeper安装ZooKeeper配置ZooKeeper命令行操作常见服务端命令 ZooKeeper的概述 什么是ZooKeeper&#xff1f; ZooKeeper是一个开源的分布式协…

FFmpeg编译4

CPUx86-64 TOOLCHAIN N D K / t o o l c h a i n s / x 8 6 6 4 − 4.9 / p r e b u i l t / l i n u x − x 8 6 6 4 S Y S R O O T NDK/toolchains/x86_64-4.9/prebuilt/linux-x86_64 SYSROOT NDK/toolchains/x866​4−4.9/prebuilt/linux−x866​4SYSROOTNDK/platforms/and…

PBR网络数据流量分流+NQA联动静态路由

一、实验目的&#xff1a; 企业有两个网段&#xff0c;业务1网段和业务2网段&#xff0c;拓扑图如下&#xff0c; 二、实验要求 pc1报文走左侧链路到达ar1&#xff0c;pc2报文走右侧链路到达ar1&#xff0c;且当ar2或者ar3发生故障时候&#xff0c;可以通过另一个设备到达ar1…

HCIA 19 结束 企业总部-分支综合实验(下)

3.6出口NAT配置可以访问互联网 配置NAT使内网可以访问公网8.8.8.8&#xff0c;当前总部PC1 PING不通公网地址8.8.8.8。 3.6.1总部配置NAT访问互联网 步骤1&#xff1a;配置NAT acl number 2000 rule 5 permit source 192.168.0.0 0.0.255.255 # interface GigabitEthern…

头条系统-05-延迟队列精准发布文章-概述添加任务(db和redis实现延迟任务)、取消拉取任务定时刷新(redis管道、分布式锁setNx)

文章目录 延迟任务精准发布文章1)文章定时发布2)延迟任务概述2.1)什么是延迟任务2.2)技术对比2.2.1)DelayQueue2.2.2)RabbitMQ实现延迟任务2.2.3)redis实现 3)redis实现延迟任务4)延迟任务服务实现4.1)搭建heima-leadnews-schedule模块4.2)数据库准备4.3)安装redis4.4)项目集成…

常用加密算法之 RSA 简介及应用

引言 相关博文&#xff1a; Spring Boot 开发 – 常用加密算法简介&#xff08;一&#xff09;常用加密算法之 SM4 简介及应用 一、RSA算法简介 RSA &#xff08;Rivest-Shamir-Adleman&#xff09; 算法是一种非对称加密技术&#xff0c;由Ron Rivest、Adi Shamir和Leonar…

基于动力学的六自由度机器人阻抗恒力跟踪控制

1.整个代码的控制流程图如下&#xff1a; 2.正逆运动学计算 略 3.动力学模型 采用拉格朗日法计算机械臂的动力学模型&#xff0c;其输入的是机械臂的关节角度、角速度和角加速度&#xff1b;其中M、C、G本别是计算的惯性力、科式力和重力项&#xff0c;相关部分如下&#xf…

【fastapi+mongodb】使用motor操作mongodb(三)

本篇文章介绍mongodb的删和改&#xff0c;下面是前两篇文章的链接&#xff1a; 【fastapimongodb】使用motor操作mongodb 【fastapimongodb】使用motor操作mongodb&#xff08;二&#xff09; delete delete 的用法基本和查找一致&#xff0c;包括delete_one&#xff08;删除…

某大厂程序员吐槽:离职交接时,新人被工作量吓退,领导却污蔑我故意劝退新人,我怒晒工作短信反击证明,新人看了后也决定走人了!

一位知名大公司的程序员分享了他离职时的遭遇&#xff1a;在交接工作时&#xff0c;新进的同事因工作量过大而感到压力&#xff0c;但出乎意料的是&#xff0c;他们的领导却指责我故意吓唬新人。为了证明自己的清白&#xff0c;我晒出了工作短信作为反击&#xff0c;结果连新人…

Vue71-嵌套(多级)路由

一、需求 二、开发步骤 2-1、编写路由组件 2-2、编写路由规则 2-3、编写路由标签<router-link>、<router-view> 三、小结

网络编程之XDP、TC和IO_URING以及DPDK

一、网络编程常见的技术 在前面已经分析过了XDP、TC和eBPF。也基本把三者间的关系理清了&#xff0c;但现在又有一个疑惑涌了上来。在前面提到过的IO_URING和DPDK与这些技术有什么关系呢&#xff1f;其实只要认真的看过分析文章可能大家心里都已经基本清楚了。 正如在前面不断…

利用golang_Consul代码实现Prometheus监控目标的注册以及动态发现与配置

文章目录 前言一、prometheus发现方式二、监控指标注册架构图三、部分代码展示1.核心思想2.代码目录3、程序入口函数剖析4、settings配置文件5、初始化配置文件及consul6、全局变量7、配置config8、公共方法目录common9、工具目录tools10、service层展示11、命令行参数12、Make…

双指针算法——部分OJ题详解

目录 关于双指针算法&#xff1a; 1&#xff0c;对撞指针 2&#xff0c;快慢指针 部分OJ题详解 283.移动零 1089.复写零 202.快乐数 11.盛水最多的容器 611.有效三角形的个数 剑指offer 57.和为s的两个数字 15.三数之和 18.四数之和 关于双指针算法&#xff1a; …

6月20日(周四)A股行情总结:A股险守3000点,恒生科技指数跌1.6%

A股三大股指走弱&#xff0c;科创板逆势上扬&#xff0c;半导体板块走强&#xff0c;多股20CM涨停。中芯国际港股涨超1%。恒生科技指数跌超1%。离岸人民币对美元汇率小幅走低&#xff0c;20日盘中最低跌至7.2874&#xff0c;创下2023年11月中旬以来的新低&#xff0c;随后收复部…

免费一年SSL证书申请——建议收藏

免费一年SSL证书申请——建议收藏 获取免费一年期SSL证书其实挺简单的 准备你的网站&#xff1a; 确保你的网站已经有了域名&#xff0c;而且这个域名已经指向你的服务器。还要检查你的服务器支持HTTPS&#xff0c;也就是443端口要打开&#xff0c;这是HTTPS默认用的。 验证域…

nlp基础-文本预处理及循环神经网络

1 认识文本预处理 1 文本预处理及其作用 定义&#xff1a;文本送给模型之前&#xff0c;提前要做的工作 作用&#xff1a;指导模型超参数的选择 、提升模型的评估指标 举个例子&#xff1a; 思路常识&#xff0c;打造成 X Y关于Y&#xff1a;10分类标签是否均衡关于X&#xf…

cesium 添加 Echarts 饼图

cesium 添加 Echarts 饼图 1、实现思路 1、首先创建echarts饼图,拿到创建好的canvas 2、用echarts里面生成的canvas添加到cesium billboard中 2、示例代码 <!DOCTYPE html> <html lang="en"><head><