并发编程 - 锁(属性修饰符 atomic)

news2024/11/15 19:31:25

引言

在多线程编程中,数据一致性是一个必须解决的问题。多个线程同时访问同一片共享数据时,极易发生竞争条件(race conditions),导致数据的不一致性,甚至程序崩溃。为了解决这些问题,我们需要引入一种机制,确保多个线程之间的操作可以安全地进行。

在Objective-C中,atomic是一种常见建的锁机制,能欧有效地防止资源竞争,保障数据的安全性。atomic常用于确保属性读写的原子性,避免数据在多线程环境下被破坏;

在本篇博客中,我们将深入探讨这种锁机制的工作原理、使用场景以及它的优缺点,并通过Objective-C的示例代码展示如何在实际项目中应用它来确保线程安全。

Atomic(属性修饰符)

概述

atomic是Objective-C中的属性修饰符,它可以确保属性读写时线程安全的。

原子属性是实现应用状态线程安全的一个良好开始,如果一个属性是使用atomic修饰的,则它的修改和读取肯定都是原子的。这一点很重要,因为这个可以阻止两个线程同时更新一个值,反之则有可能导致错误的状态。正在修改属性的线程必须处理完毕之后,其它线程才能开始处理。

在Objective-C中,属性默认是atomic的,这意味着编译器会自动为属性的getter和setter方法加锁,确保属性的读写操作时线程安全的。然而,如果我们显示地将属性标记为nonatomic,编译器就不会为我们提供这种保护。虽然这可以提升一些性能,但也意味着在并发环境下,多线程可能同时读取和写入该属性,从而导致数据竞争(race condition)。

nonatomic 多线程操作

在这个示例中,我们使用一个nonatomic修饰的属性address,并通过GCD创建了多个并发线程来设置和打印这个属性的值:

//MARK: nonatomic修饰的属性
- (void)nonatomicTest {
    for (int i = 0; i < 1000; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.address = [NSString stringWithFormat:@"河北省唐山市路北区%d", i];
            NSLog(@"%@", self.address);
        });
    }
}

因为addres是nonatomic的,所以在多线程同时写入时,并不能保证数据的完整性和一致性。可能会发生一些异常情况,比如输出的地址内容会出现错乱,或者值还没有完全写入就被领一个线程读取,从而导致不完整的输出,或者崩溃。通常会崩溃在objec_release或者objct_retian相关的操作中。

atomic 多线程操作

而如果我们使用atomic来修饰address属性,则可以保证它的读写操作是线程安全的。

//MARK: atomic修饰的属性
- (void)atomicTest {
    for (int i = 0; i < 1000; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.address = [NSString stringWithFormat:@"河北省唐山市路北区%d", i];
            NSLog(@"%@", self.address);
        });
    }
}

由于atomic修饰符的影响,它保证了对该属性的每次读写操作都是线程安全的。也就是说,在多线程环境中,访问address属性时,每次的读写操作都不会被其他线程打断。

编译器会自动为address的getter和setter方法加锁,确保在并发操作时数据的完整性和一致性。

但是atomic虽然确保了线程安全,但它引入了额外的开销,每次对address的读写都需要加锁和解锁,这可能会对性能产生一定影响,尤其是在高并发的情况下。

atomic局限

atomic虽然可以保证单个数据的读写是线程安全的,但是却不能保证复合数据的一致性,原子属性通常作用于单个变量或数据操作,对于多个数据,或者数组并不起作用,因为数组通常包含多个元素,原子操作无法直接应用整个数组。

假设我们现在有一个HPUser实体类,通过服务类可以对其数据进行更新。

//一个实体(部分定义)
@interface HPUser

@property(atomic,copy)NSString * firstName;
@property(atomic,copy)NSString * lastName;

@end
//一个服务类(出于简洁的目的省略了声明)
@implementation HPUpdaterService

- (void)updateUser:(HPUser *)user properties:(NSDictionary *)properties{
    NSString * fn = [properties objectForKey:@“firstName”];
    if(fn != nil){
        user.firstName = fn;
    }
    NSString * ln = [properties objectForKey:@“lastName”];
     if(fn != nil){
        user.lastName = ln;
    }
}

@end

每当用户刷线数据,数据从服务器返回时都会调用updateUser:properties:方法。此外,一个周期性执行的同步任务也会调用该方法。

因此,在某个时间点可能会有多个响应同时尝试更新用户配置文件。

当有两个响应在不同的线程中试图更新用户,名称分别为Bob Taylor和Alice Darji。如果不对属性firstName和lastName使用原子更新,则根本无法确保执行顺序,最终的结果可能是任意组合,其中包括Alice Taylor和Bob Darji。

这时候我们就看见了atomic原子属性的局限性,但我们仍然有其它方案可以解决这个问题,我们会在后面的博客中继续讨论它。

结语

在这篇博客中,我们深入探讨了并发编程中的锁机制,主要是Objective-C中的atomic属性。通过对atomic属性的详细分析,我们了解了它如何确保在多线程环境中的线程安全性。以及它在保证数据一致性方面的作用和性能开销。

然而,atomic并不是解决所有线程安全问题的终极方案。在一些复杂的并发场景中,可能需要更灵活的同步机制来确保多个线程间的协调和数据一致性。接下来,我们将探索另一种强大的同步工具——@synchronized。

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

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

相关文章

Vulnhub:BlueSky

靶机下载地址 信息收集 主机发现 nmap扫描攻击机同网段存活主机。 nmap 192.168.31.0/24 -Pn -T4 靶机ip&#xff1a;192.168.31.171。 端口扫描 nmap 192.168.31.171 -A -p- -T4 开放端口22,8080。 目录扫描 访问8080端口&#xff0c;如图&#xff0c;是tomcat管理页面…

Vue3.0组合式API:使用reactive()、ref()创建响应式代理对象

1、reactive() 方法 reactive() 方法用于将定义的 JavaScript 对象转换为响应式对象。 使用方法&#xff1a; <script setup> //第一步&#xff1a;导入函数 import { reactive } from vue;//第二步&#xff1a;创建响应式对象 const data reactive(对象类型的数据);…

prompt实用技巧-AI+Mermaid【酷炫钉钉文档】

AI 新技能&#xff0c;最近 chatGPTo1 发布后模型能力出现了新的跨越&#xff0c;之前模型的一本正经的胡说八道幻想模式&#xff0c;让AI 对待理科推理明显弱于文案的 AGI 的生成。 prompt engineer 工程师程序员的福音 prompt 内容如下&#xff0c; 按照以上格式生成创建公…

C++ Primer Plus(速记版)-类和数据抽象

第十二章 类 在 C 中&#xff0c;用类来定义自己的抽象数据类型。 12.1. 类的定义和声明 12.1.1. 类定义&#xff1a;扼要重述 最简单地说&#xff0c;类就是定义了一个新的类型和一个新作用域。 类成员 类成员可以是属性、方法或类型别名。 成员可以是公有&#xff08;publ…

CPU 缓存的局部性特性

CPU 缓存的局部性特性是现代处理器中用来提高程序执行效率的重要原理。局部性特性是指程序在运行时&#xff0c;其数据和指令的访问具有一定的规律性&#xff0c;主要体现在时间局部性和空间局部性两方面。利用这些规律&#xff0c;CPU 可以更高效地管理缓存&#xff0c;减少从…

github远程仓库环境搭建及使用

目录 1、创建一台虚拟机 centos 源的配置 备份源 修改源 重新加载缓存 安装软件 配置epel 2、关闭防火墙和selinux 关闭防火墙 临时关闭SELinux 永久关闭SELinux&#xff1a;编辑SELinux的配置文件 配置文件的修改内容 3、git是本地仓库&#xff0c;linux系统中一…

CleanClip:Mac上的剪贴板神器,轻松追溯历史记录

CleanClip 是一款功能强大的 Mac 剪贴板历史管理工具,它可以帮助用户轻松地管理和追溯复制粘贴的历史记录。CleanClip 能够保存用户复制过的所有内容,包括文本、图片、文件等,并且提供了多种视图模式,包括列表视图和网格视图,方便用户快速找到需要的内容。 评论区获取软件 Cl…

查询中的算术表达式

需要修改数据显示方式&#xff0c;如执行计算&#xff0c;或者作假定推测&#xff0c;这些都可能用到算术表达式。 一个算术表达式可以包含列名、固定的数字值和算术运算符。 使用算术运算符 示例&#xff1a;查询雇员的年薪&#xff0c;并显示他们的雇员ID&#xff0c;名字…

【计算机网络】应用层序列化

目录 一、序列化和反序列化二、重新理解 read、write、recv、send 和 tcp 为什么支持全双工三、Jsoncpp 一、序列化和反序列化 如果我们要实现一个网络版的加法器&#xff0c;需要把客户端的数据发给服务端&#xff0c;由服务端处理数据&#xff0c;再把处理结果发给客户端。 …

【线程】线程的概念

本文重点&#xff1a; 了解线程概念&#xff0c;理解线程与进程区别与联系 线程的概念 线程是进程内的一个执行分支&#xff0c;执行粒度要比进程细 乍一看&#xff0c;肯定不懂&#xff0c;太抽象了&#xff0c;怎么理解呢&#xff1f; 以前讲的进程&#xff0c;每个进程…

spring boot设置多环境的配置文件

目录 说明规划步骤案例application.ymlapplication-dev.ymlapplication-test.ymlapplication-prod.yml 说明 在项目开发过程中&#xff0c;回有开发环境、测试环境、生产环境等等。如果所有环境的配置都放在application.yml中并且需要打包到不同的环境中时&#xff0c;修改app…

还在用旧的电脑屏保?新一代电脑屏保你见识过吗?

还在用旧的电脑屏保&#xff1f;新一代电脑屏保你见识过吗&#xff1f;旧的电脑屏保我们已经见怪不怪了&#xff0c;没有什么新颖的效果&#xff0c;而新一代的电脑屏保在视觉上做了很多突破。一个好看的屏保&#xff0c;会让你的电脑变得更加美观&#xff0c;更有特色。哪里才…

SpringSecurity原理解析(七):权限校验流程

SpringSecurity是一个权限管理框架&#xff0c;核心功能就是认证和授权&#xff1b;前边已经介绍了认证流程&#xff0c; 这里看下授权的流程和核心类。 一、权限管理的实现 1、开启对权限管理的注解控制 工程中后端各种请求资源要想被SpringSecurity的权限管理控制&#xf…

八、4 DMA+AD多通道(代码)

一、&#xff08;1&#xff09;接线图 PA0接一个电位器&#xff0c;PA1~PA3接3个传感器的AO输出 &#xff08;2&#xff09;流程 二、代码部分 &#xff08;1&#xff09;复制AD多通道的工程 将DMA初始化的部分复制到ADC_Cmd之前 将开启DMA时钟的函数挪到前面 &#xff08;…

人工智能——猴子摘香蕉问题

一、实验目的 求解猴子摘香蕉问题&#xff0c;根据猴子不同的位置&#xff0c;求解猴子的移动范围&#xff0c;求解对应的过程&#xff0c;针对不同的目标状态进行求解。 二、实验内容 根据场景有猴子、箱子、香蕉&#xff0c;香蕉挂天花板上。定义多种谓词描述位置、状态等…

Linux入门1

文章目录 一、Linux的认识和构建1.1 初始Linux1.2 虚拟机1.3 Final Shell1.4 虚拟机快照 二 、Linux的相关知识2.1 Linux目录结构2.2 linux的基本命令 一、Linux的认识和构建 1.1 初始Linux 相比于其他的操作系统&#xff0c;Linux操作系统是在服务器端应用最为广泛和普遍被认…

力扣最热一百题——螺旋矩阵

目录 题目链接&#xff1a;54. 螺旋矩阵 - 力扣&#xff08;LeetCode&#xff09; 题目描述 示例 提示&#xff1a; 解法一&#xff1a;模拟 1. 边界初始化 2. 循环遍历矩阵 3. 从左到右遍历上边界 4. 从上到下遍历右边界 5. 从右到左遍历下边界 6. 从下到上遍历左边…

2.1 溪降技术:溪降运动的定义

目录 2.1 溪降运动的定义概览观看视频课程电子书: 溪降运动的定义**“溪降”&#xff08;Canyoning&#xff09;还是“峡谷探险”&#xff08;Canyoneering&#xff09;&#xff1f;**湿峡谷与干峡谷干峡谷**湿峡谷** 总结 2.1 溪降运动的定义 概览 溪降(新西兰) 溪降是一种在非…

P5425 [USACO19OPEN] I Would Walk 500 Miles G

*原题链接* 很离谱的题。首先可以想到暴力连边&#xff0c;整个图为一个完全图&#xff0c;将所有的边选出来&#xff0c;然后从小到大一条条加入&#xff0c;当剩下集合数量 <K 的时候就结束。答案为加入的最后一条边的大小。如果用prim算法的话时间复杂度为。足以通过此题…

yjs05——matplotlib画其他图像

不管是折线图还是散点图&#xff0c;饼状图&#xff0c;柱状图等&#xff0c;其流程都是 1.创建幕布 ❤2.画图画坐标补充信息 3.保存图像 4.展示图像 不同就是在画图时候的代码不太相同 折线&#xff1a;plt.plot(x,y) 散点&#xff1a;plt.scatter() 柱状图&#xff1a;plt.hi…