3.1线程之间共享数据的问题

news2024/9/23 7:19:47

线程之间共享数据的问题

从整体上来看,所有线程之间共享数据的问题,都是修改数据导致的。如果所有的共享数据都是只读的,就没有问题,因为一个线程所读取的数据不受另一个线程是否正在读取相同的数据而影响。然而,如果数据是在线程之间共享的,同时一个或多个线程开始修改数据,就可能有很多的麻烦。在这种情况下,你必须要小心确保一切安好。

一个被广泛用来帮助程序员推导代码的概念,就是不变量——对于特定的数据结构总是为真的语句,例如“此变量包含了列表中项目的数量。”这些不变量在更新中经常被打破,尤其是在数据结构比较复杂或是更新需要修改超过一个值时。

考虑一个双向链表,它的每一个节点持有指向表中下一节点和前一节点的指针。其中一个不变量就是如果你跟随从一个节点(A)到另一个节点(B)的“下一个”指针,则那个节点(B)的“前一个”指针指回到前一个节点(A)。为了从表中删除一个节点,两边的节点都必须被更新为指向彼此。一旦其中一个被更新,直到另一侧的节点也被更新前不变量是打破的,当更新完成后,再次持有不变量。

从这样的表中删去一个条目的步骤如图3.1所示。
在这里插入图片描述
1)标识要删除的节点(N)。
2)将N的前一节点到N的链接更新为指向N的后一节点。
3)将N的后一节点到N的链接更新为指向N的前一节点。
4)删除节点N。

如你所见,在步骤b和c之间,在一个方向上的链接与在相反的方向上的链接不一致,并且不变量损坏。

修改线程之间共享数据的最简单的潜在问题就是破坏不变量。如果你没有为确保其他情况而做些特别的工作,要是一个线程正在读取双向链表,而另一个线程正在删除一个节点,那么读线程很有可能看到一个节点仅被部分删除了的链表(因为在图3.1的步骤b中,只有其中一个链接被改变了),因此不变量损坏。不变量损坏的后果可能有所不同,如果其他线程只是在图中由左到右读取链表项,它会跳过正被删除的节点。另一方面,如果又一个线程试图删除图中最右边的节点,则可能最终永久性破坏数据结构,并使得程序崩溃。无论结果如何,这是并发代码中错误的最常见诱因之一的例子:竞争条件

竞争条件

假设你在电影院买票看电影。如果是个大电影院,会有多个收银员收款,所以不止一个人可以同时买票。如果有人在另一个收银台也购买了与你同一部电影的票,这时可供你选择的座位取决于事实上是其他人先订购还是你先订购。如果只剩少量座位,这种差异可能会很关键。字面上可以看作一个比赛,看谁得到最后的电影票。这是一个竞争条件的例子:得到哪个座位(或者甚至是否得到票)取决于两次购买的相对顺序。

在并发性中,竞争条件就是结果取决于两个或更多线程上的操作执行的相对顺序的一切事物。线程竞争执行各自的操作。在大多数情况下,这是比较良性的,因为所有可能的结果都是可以接受的,尽管他们可能会随着不同的相对顺序而改变。例如,如果两个线程都将项目添加到一个队列中等待处理,在保持系统不变量的前提下,哪个项目先被添加一般是不影响的。当竞争条件导致损坏不变量时才会出现问题,以刚才提到的双向链表为例。在谈到并发时,术语竞争条件通常用来表示有问题的竞争条件。良性的竞争条件没什么意思,也不是错误的诱因。C++标准还定义了术语数据竞争,表示因对单个对象的并发修改而产生的特定类型的竞争条件,数据竞争造成可怕的未定义行为

有问题的竞争条件通常发生在完成操作需要修改两个或多个不同的数据块的地方,就如示例中的两个链表指针。因为该操作必须访问两块独立的数据,这必须在单独的指令中进行修改,而当只有其中一条指令完成时,另一个线程有可能访问此数据结构。竞争条件往往很难找到且难以复制,因为机遇的窗口很小。如果这些修改作为连续的CPU指令来完成,在任意一次运行中显现问题的机会是非常小的,即使数据结构正被另一个线程并发访问。随着系统上负载的升高,以及执行该操作次数的增加,有问题的执行序列出现的机会也增加。这种问题几乎是不可避免地会在最不方便的时间暴露出来。由于竞争条件一般是时间敏感的,它们常常在应用程序运行于调试工具下时完全消失,因为调试工具会影响程序的时间,即使只是轻微地。

如果你正在编写多线程程序,竞争条件会轻易地成为你生活的灾难。编写使用并发的软件中大量的复杂性来源于避免有问题的竞争条件。

避免有问题的竞争条件

有几种方法来处理有问题的竞争条件。最简单的选择是用保护机制封装你的数据结构,以确保只有实际执行修改的线程能够在不变量损坏的地方看到中间数据。从其他访问该数据结构线程的角度看,这种修改要么还没开始要么已完成。C++标准库提供了一些这样的机制,在本章中均有述及。

另一个选择是修改数据结构的设计及其不变量,从而令修改作为一系列不可分割的变更来完成,每个修改均保留其不变量。这通常被称为无锁编程,且难以尽善尽美。如果你工作在这个级别上,内存模型的细微差异和确认哪些线程可能看到哪组值,会变得很复杂。

处理竞争条件的另一种方式是将对数据结构的更新作为一个事务来处理,就如同在一个事务内完成数据库的更新一样。所需的一系列数据修改和读取被存储在一个事务日志中,然后在单个步骤中进行提交。如果该提交因为数据结构已被另一个线程修改而无法进行,该事务将重新启动。这称为软件事务内存(STM),在写作时这是一个活跃的研究领域。这在本书中不会述及,因为在C++中没有对STM的直接支持。然而,私下里做些事情然后在单个步骤中提交的基本思想,我会在后面提到。

由C++标准提供的保护共享数据的最基本机制是互斥元(mutex),那么我们先来看一看。

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

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

相关文章

慧算账的千亿财税市场之旅,从当好中小企业财税之友开始

数字化转型浪潮下,各个行业都在不断升级进化。不过,各领域中头部企业与中小企业数字化的方式有所不同。头部企业资金实力雄厚,对于数字化架构的搭建,往往会以内设部门加外部合作的方式来推进,而中小企业在可投入研发资…

Vscode无法写入文件 NoPermissions (FileSystemError): Error: EACCES: permission

用Vscode想要新建一个index.html的时候遇到了下图问题,说没有权限无法写入文件。 没有权限,咱们给他加上权限哈哈哈,博主是Mac电脑,如下操作: 1.找到你项目的根目录,右键,点击“显示简介”。 …

2023华数杯C题总结

前言 对这次比赛中遇到的问题和卡住的思路进行复盘,整理相关心得,供以后比赛参考 🧡1.认识数据类型🧡 连续变量:母亲年龄、妊娠时间、CBTS、EPDS、HADS、整晚睡醒时间、婴儿年龄 无序分类变量:婚姻状态、…

Java中ArrayList常用方法的学习

Java中ArrayList常用方法的学习 需求分析代码实现小结Time 需求分析 ArrayList集合的常用方法学习 代码实现 java.util.ArrayList;/*** Author:LQ* Description:* Date:Created in 16:45 2023/8/9*/ public class ListTest {public static void main(String[] args) {ArrayLis…

哪个版本的FL Studio更适合我?2023年到底应该入手哪一款FL Studio?

很多打算入手正版FL Studio的新手朋友都会纠结一个问题:哪个版本的FL Studio更适合我,2023年到底应该入手哪一款FL Studio?本文会介绍每个版本之间的差异点,并带大家选择适合自己的FL Sudio版本。 FL Studio Mac-安装包&#xff…

B2B2C线上直播购物商城开源搭建--多语言+自带商品库

要搭建一个B2B2C线上直播购物商城,可以考虑以下步骤: 1. 确定技术需求:确定前端和后端使用的技术栈。 2. 搭建基础环境:购买一个域名和服务器空间,安装和配置相应的操作系统和数据库。 3. 开发商城平台:…

LeetCode150道面试经典题--罗马数字转整数(简单)

目录 1.题目 2.示例 3.思路 4.案例代码(Java) 1.题目 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。 字符 数值 I 1 V 5 X 10 L …

UE 5 GAS Gameplay Ability System

游戏技能系统 简称(GAS),是一个健壮的,高度可扩展的gameplay框架,通常用于构件RPG、MOBA等游戏的完整战斗逻辑框架。 通过GAS,可以快速地制作游戏中的主动/被动技能、各种效果Buff、计算属性伤害、处理玩家…

openGauss学习笔记-31 openGauss 高级数据管理-索引

文章目录 openGauss学习笔记-31 openGauss 高级数据管理-索引31.1 语法格式31.2 参数说明31.3 示例 openGauss学习笔记-31 openGauss 高级数据管理-索引 索引是一个指向表中数据的指针。一个数据库中的索引与一本书的索引目录是非常相似的。 索引可以用来提高数据库查询性能&…

我在VScode学Java多态(Java多态、instanceof)

Java的多态(Polymorphism)是面向对象编程中的一种特性,它允许不同的对象能够以统一的方式进行访问和操作。它允许一个类的实例在运行时表现出多种形态。 Java多态的实现主要依赖于两个基本概念:继承和方法重写。在Java中&#xff…

程序员的趣闻:神奇Bug与不可思议的技术世界

文章目录 Bug本身情况发现Bug的过程怎么对待这个Bug其他感受 程序员的世界充满了各种离奇而又难以置信的趣闻,其中不乏那些令人目瞪口呆的神奇Bug。有时候,这些故事不仅令人捧腹大笑,还展现了技术世界的多姿多彩。让我们走进这个充满惊奇的领…

Java集合容器详解:ArrayList、LinkedList和HashMap、HashTable及其区别

文章目录 一、简介二、ArrayList详解2.1 动态数组2.2 扩容机制2.3 特点2.4 操作 三、LinkedList详解3.1 双向链表结构3.2 双向链表结构3.3 操作 四、HashMap详解4.1 概述4.2 内部实现4.2.1 哈希表结构4.2.2 散列冲突解决4.2.3 扩容机制 4.3 版本差异4.4 实操 五、HashTable5.1 …

安卓:LitePal操作数据库

目录 一、LitePal介绍 常用方法: 1、插入数据: 2、更新数据: 3、删除数据: 4、查询数据: 二、LitePal的基本用法: 1、集成LitePal: 2、创建LitePal配置文件: 3、创建模型类…

Vue+Vue Router+TailwindCss+Daisyui部署

一、构建Vue项目 > npm init vuelatest > cd <your-project-name> > npm install > npm run dev 二、设置IDEA JS版本 三、安装Tailwindcss Install Tailwind CSS with Vite - Tailwind CSS npm install -D tailwindcss postcss autoprefixer npx tai…

Linux下匿名管道简单模拟进程间通信

Linux下匿名管道简单模拟进程间通信 文章目录 Linux下匿名管道简单模拟进程间通信在这里插入图片描述1.引言2.具体实现2.1创建管道2.2创建子进程 && 通信(子进程写入)2.3关闭对应fd 3.结果 1.引言 ​ ​ 首先&#xff0c;管道是一种半双工的单向进程间通信方式&#…

有哪些简单的AI绘画软件?

随着人工智能技术的不断发展&#xff0c;越来越多的人工智能绘画软件出现了。人工智能绘画软件利用人工智能技术&#xff0c;通过计算机自动生成或辅助生成艺术作品。人工智能绘画软件通常集成了深度学习、计算机视觉和自然语言处理技术&#xff0c;可以模拟人类的创作过程&…

【数据结构与算法】十大经典排序算法-插入排序

&#x1f31f;个人博客&#xff1a;www.hellocode.top &#x1f3f0;Java知识导航&#xff1a;Java-Navigate &#x1f525;CSDN&#xff1a;HelloCode. &#x1f31e;知乎&#xff1a;HelloCode &#x1f334;掘金&#xff1a;HelloCode ⚡如有问题&#xff0c;欢迎指正&#…

【Shell】基础语法(三)

文章目录 一、Shell基础语法1. 位置参数和特殊变量2. 输入输出3. 管道4. 文件重定向5. 函数6. 脚本调试方法 二、Shell高级和正则表达式1. sort命令2. uniq命令3. wc命令4. grep命令5. find命令6. xargs7. sed命令8. crontab 一、Shell基础语法 1. 位置参数和特殊变量 $0 …

循环队列详解

1. 循环队列 1.1 概念及结构 循环队列是一种特殊类型的队列数据结构&#xff0c;也被称为”唤醒缓冲器“。它在数组的基础上实现了循环利用空间的功能。在循环队列中&#xff0c;队尾和队头之间形成了一个循环&#xff0c;当队尾指针“追上”队头指针时&#xff0c;队列不再继…