当前位置: 首页 > news >正文

为什么需要锁——多线程的数据竞争是怎么引发错误的

在进行多线程编程时,如果两个线程可能同时访问一个对象,我们会不加思考地在访问这个对象前加锁。本篇想仔细考虑一下加锁的目的。有时我会糊涂,我会这么想:比如一个线程往共享队列里 push,另一个线程读队列中的数据(只读不写),我会不明白这时候为什么需要加锁,读线程要么读到写线程更新后的值,要么写线程更新了,读线程还没有同步到数据,那就下次读的时候再读新数据就好了,我们也不要求写线程更新后读线程在多长时间内读到啊。其实这里是有个很简单很基础的误区的。

下面我罗列一些数据竞争的引发错误,分析“错误”到底是怎么出现的(肯定不全,不时补充)

读改写问题

在教学时最常见,实际上几乎遇不到的教学用数据竞争错误。两个线程对同一个变量进行++操作(x++,x 是 int)。老生常谈,++ 并非一条原子指令,而是先把内存值读到寄存器,修改寄存器,再把寄存器的值写回内存。硬件决定了++是一个过程,在这个过程中另一个线程可能会修改变量值,那么++线程+1的操作就基于了错误的旧值,最终的计算结果肯定也就错误了。这里的关键点在于++操作是一个过程,该过程基于的原始条件可能会在过程中被更改,导致过程的结果无效。

但还是会糊涂,比如我程序员在写 x++ 时,如果知道其他线程可能会更改,导致 x 并非 ++ 前的值 +1。那么上述问题就不是一个错误,只是预期中的现象。只不过可能很少有情况能容忍程序这么运行,这程序就很难可控了。我只是想强调这里的错误是指和预期的不符,而不是说硬件的崩溃,只要程序行为和预期相符,就没有错误可言了。

比如如果多个线程不加以同步的设置某一个变量的值,比如设置一个 int 变量,那么最终这个 int 变量肯定是某个线程最终设置的值,而不会是一个任何线程都没有设置的值。因为设置 int 变量这个操作是原子的,即使在硬件层面也根本没有过程。

读异常问题

再说一个比较实际的,两个线程,一个往共享队列里加元素,另一个读(只读不写),就是开篇说的那种情况。这里往往默认会加锁,但不加锁有什么问题呢?问题在于这里的“往共享队列里加元素”的写操作并非原子的,这是我们可以基于常识判断出来的。那么加元素本身就是一个过程,过程的初始和结尾都是正确的状态,而过程的中间就不一定了。多线程的调度又是不可控的,如果在这个写过程中间调度执行读线程,那么读出来的结果就不一定正确了。这就引发了错误。

即使不用严谨的分析,我们也愿意相信,对一般的容器、对象等的写操作基本不是原子的,它很可能会导致一个错误的中间状态。所以这时如果不加同步(锁)地与其他读线程合作的话,那读线程就可能读到错误的中间状态。这里的错误也是指的程序执行和预期不符,也不会出现任何的硬件崩溃哈。

而且一般也少有单纯的读线程吧,如果共享对象用的是 C++ 容器,程序员也很难保证逻辑上的读端不会改容器中的属性吧,那么是不是又容易涉及到读改写的问题,甚至更可能的是两个“写过程”同时执行,互相覆盖一部分对方写的内容,那最后内存里的值就是两个写过程的结果的糅合,是不是就更错误了。

http://www.dtcms.com/a/363578.html

相关文章:

  • 梯度消失问题:深度学习中的「记忆衰退」困境与解决方案
  • 从C语言入门到精通:代码解析与实战
  • 零知开源——STM32红外通信YS-IRTM红外编解码器集成灯控与显示系统
  • Obsidian本地笔记工具:构建知识网络关联笔记,支持Markdown与插件生态及知识图谱生成
  • 95%企业AI失败?揭秘LangGraph+OceanBase融合数据层如何破局!​
  • 【前端面试题✨】Vue篇(一)
  • 【XR技术概念科普】什么是注视点渲染(Foveated Rendering)?为什么Vision Pro离不开它?
  • 使用gsoap实现简单的onvif服务器:1、编译
  • SpringBoot 整合 RabbitMQ 的完美实践
  • @ZooKeeper 详细介绍部署与使用详细指南
  • 网站搭建应该选择什么服务器?
  • 人体姿态估计与动作分类研究报告
  • 四.shell脚本编程
  • 在时间序列中增加一个阶跃对长期趋势变化的影响
  • 大批量文件管理操作的linux与windows系统命令行终端命令
  • Linux内核进程管理子系统有什么第四十回 —— 进程主结构详解(36)
  • 【网络安全入门基础教程】网络安全行业,未来两年就业和再就业都会很难
  • git: 取消文件跟踪
  • Linux Shells
  • ubuntu24.04网络无法访问(网络问题)NAT网关写错了
  • MSVC, GCC, Clang
  • playwright+python 实现图片对比
  • Linux 进程信号补充知识点总结(按重要程度排序)
  • 立足稳联技术的Ethernet IP转ModbusTCP网关与触摸屏连接案例的专业研判
  • Web3 出海香港 101 |BuildSpace AMA 第一期活动高亮观点回顾
  • C++全局变量未初始的和已初始化的位置放在哪里?
  • Web3兴起:重新定义互联网格局
  • 强化学习PPO/DDPG算法学习记录
  • 图像编码之摄像机的H264 分块编码的含义是什么,以分块编码(tile)192X192为例子说明,好处与缺点分别是什么
  • Day19(前端:JavaScript基础阶段)