c++开发基础之保障多线程编程中的原子操作InterlockedIncrement和InterlockedDecrement用法详解

一、介绍

在多线程编程中,确保对共享变量进行原子操作是至关重要的。当多个线程同时访问和修改同一共享资源时,如果没有合适的同步机制,可能会导致数据竞争、内存一致性问题,甚至造成程序崩溃。为了解决这个问题,C++提供了一组原子操作函数,其中包括InterlockedIncrement和InterlockedDecrement。本文将深入探讨这两个函数的用法,以及它们在多线程环境中的重要性。

二、概念

InterlockedIncrement 和 InterlockedDecrement 是 Windows API 中的函数,用于对整型变量进行原子增加和减少操作。它们可以保证在多线程环境下对变量进行原子操作,避免因竞争条件而导致的数据错误。

以下是关于这两个函数的用法说明:

InterlockedIncrement

在这里插入图片描述

LONG InterlockedIncrement(LONG volatile *Addend
);
  • 参数 Addend:要进行递增操作的变量的指针。
  • 返回值:递增后的值。

InterlockedIncrement 函数将给定变量的值增加 1,并返回递增后的值。该函数是原子操作,可以确保在多线程环境下不会出现竞争条件。

示例用法:

#include <Windows.h>LONG counter = 0;void IncrementCounter()
{LONG result = InterlockedIncrement(&counter);// 对 counter 的递增操作已完成
}

InterlockedDecrement

在这里插入图片描述

LONG InterlockedDecrement(LONG volatile *Addend
);
  • 参数 Addend:要进行递减操作的变量的指针。
  • 返回值:递减后的值。

InterlockedDecrement 函数将给定变量的值减少 1,并返回递减后的值。与 InterlockedIncrement 类似,该函数也是原子操作,适用于多线程环境。

示例用法:

#include <Windows.h>LONG counter = 0;void DecrementCounter()
{LONG result = InterlockedDecrement(&counter);// 对 counter 的递减操作已完成
}

需要注意的是,InterlockedIncrement 和 InterlockedDecrement 函数只能用于操作长整型(LONG)变量。如果要对其他类型的变量进行原子操作,可以考虑使用其他同步机制,如互斥锁(mutex)或原子操作类(std::atomic)。此外,在使用这些函数时也需要注意避免竞争条件和正确处理线程同步,以确保数据的正确性和一致性。

三、思考

  1. 背景/问题陈述

在多线程编程中,当多个线程同时对共享变量进行读写操作时,可能会发生竞态条件(Race Condition)和数据竞争(Data Race)。竞态条件指的是多个线程并发执行时,最终执行结果的正确性取决于线程执行的相对时序,这可能导致不确定的行为。数据竞争指的是多个线程同时访问共享内存,至少其中一个线程在写入数据,且没有同步机制来确保正确的执行顺序,从而导致数据不一致性。

  1. 解决方案/方法

InterlockedIncrement和InterlockedDecrement函数是Windows平台提供的原子操作函数,用于对32位整数进行原子递增和递减操作。它们能够确保对共享变量的操作是原子性的,即在执行操作期间不会被中断或干扰。这样一来,即使多个线程同时访问同一个共享变量,也不会出现竞态条件或数据竞争问题。

  1. 示例代码/案例分析

假设有一个全局变量int counter,多个线程同时对这个计数器进行递增和递减操作,如果不使用原子操作,可能会出现竞态条件和数据竞争的问题。

#include <windows.h>
#include <iostream>
#include <thread>
#include <vector>// 全局变量
int counter = 0;// 递增函数
void IncrementCounter() {for (int i = 0; i < 1000000; ++i) {counter++; // 非原子操作,可能导致竞态条件和数据竞争}
}// 递减函数
void DecrementCounter() {for (int i = 0; i < 1000000; ++i) {counter--; // 非原子操作,可能导致竞态条件和数据竞争}
}int main() {std::vector<std::thread> threads;// 创建多个线程进行递增操作for (int i = 0; i < 5; ++i) {threads.emplace_back(IncrementCounter);}// 创建多个线程进行递减操作for (int i = 0; i < 5; ++i) {threads.emplace_back(DecrementCounter);}// 等待所有线程执行完毕for (auto& thread : threads) {thread.join();}// 输出计数器的值std::cout << "Counter value: " << counter << std::endl;return 0;
}

在上面的示例中,多个线程同时对计数器进行递增和递减操作,由于counter++counter--不是原子操作,可能会导致竞态条件和数据竞争的发生,从而导致计数器的最终值不确定。

为了解决这个问题,我们可以使用InterlockedIncrement和InterlockedDecrement函数,将计数器的递增和递减操作改为原子操作,如下所示:

#include <windows.h>
#include <iostream>
#include <thread>
#include <vector>// 全局变量
LONG counter = 0;// 递增函数
void IncrementCounter() {for (int i = 0; i < 1000000; ++i) {InterlockedIncrement(&counter); // 原子递增操作}
}// 递减函数
void DecrementCounter() {for (int i = 0; i < 1000000; ++i) {InterlockedDecrement(&counter); // 原子递减操作}
}int main() {std::vector<std::thread> threads;// 创建多个线程进行递增操作for (int i = 0; i < 5; ++i) {threads.emplace_back(IncrementCounter);}// 创建多个线程进行递减操作for (int i = 0; i < 5; ++i) {threads.emplace_back(DecrementCounter);}// 等待所有线程执行完毕for (auto& thread : threads) {thread.join();}// 输出计数器的值std::cout << "Counter value: " << counter << std::endl;return 0;
}

在这个修改后的示例中,通过使用InterlockedIncrement和InterlockedDecrement函数,将计数器的递增和递减操作改为原子操作,确保了对共享变量的安全访问,避免了竞态条件和数据竞争的发生。

  1. 注意事项/优化建议

尽管InterlockedIncrement和InterlockedDecrement函数能够提供较高的性能和效率,但仍需注意以下事项:

  • 这些函数仅适用于32位整数类型。
  • 在多线程编程中,除了原子操作外,还需要考虑其他同步机制,如互斥锁、条件变量等,以确保程序的正确性和性能。
  • 在实际应用中,建议进行合适的性能测试和优化,以便找到最优的解决方案。

四、应用场景

InterlockedIncrement和InterlockedDecrement函数在多线程编程中的应用非常广泛,特别是在需要对共享变量进行原子递增和递减操作的场景。

  1. 计数器操作:在多线程环境中,经常需要对计数器进行操作,例如统计某个事件发生的次数或者对资源的使用情况进行跟踪。使用InterlockedIncrement和InterlockedDecrement函数可以确保对计数器的操作是原子性的,避免了多个线程同时对计数器进行修改而导致的数据竞争。

  2. 资源管理:在多线程程序中,可能会存在多个线程共享同一资源的情况,如共享内存区域、文件句柄等。使用InterlockedIncrement和InterlockedDecrement函数可以有效地对资源的引用计数进行增减操作,确保在资源被释放前不会出现资源被误释放的情况。

  3. 线程同步:在某些场景下,需要对线程的数量进行动态管理,例如线程池中的线程数控制。通过使用InterlockedIncrement和InterlockedDecrement函数,可以实现对线程数量的原子增减操作,避免了在高并发情况下出现线程数不一致的问题。

  4. 任务分配:在任务调度或者工作队列中,经常需要对任务进行分配和执行。使用InterlockedIncrement和InterlockedDecrement函数可以实现对任务计数的原子操作,确保任务被正确地分配和执行,避免了任务重复执行或者丢失的情况。

  5. 锁的计数:在一些情况下,可能需要实现可重入锁(Reentrant Lock),即同一个线程可以多次获取同一把锁而不会造成死锁。通过使用InterlockedIncrement和InterlockedDecrement函数来对锁的计数进行增减操作,可以实现可重入锁的功能。

五、结论

InterlockedIncrement和InterlockedDecrement函数作为原子操作函数,在多线程编程中扮演着重要的角色。它们提供了一种简单而有效的方式来执行原子递增和递减操作,从而确保多线程程序的正确性和可靠性。合理地使用这些函数,可以有效地避免数据竞争和内存一致性问题,提高程序的稳定性和性能。

六、参考资料

  • Microsoft Documentation: InterlockedIncrement function
  • Microsoft Documentation: InterlockedDecrement function
  • C++ Concurrency in Action by Anthony Williams

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

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

相关文章

公众号,h5 链接直接在浏览器打开 拒绝下载视频解决方案

公众号&#xff0c;h5 链接直接在浏览器打开 拒绝下载视频 1.微信打开公众号 2.在微信上打开浏览器 3.F12打开页面 4.播放视频 5.找到video标签代码 6.去掉video标签的属性controlslist“nodownload” 7.全屏播放 8.下载

【个人博客搭建】butterfly主题配置

目录 一、基础配置 (一) 模板配置 1. 文章模板 2. 页面模板 (二) 创建页面和文章 1. 标签页【可选】 2. 分类页【可选】 3. 友链页【可选】 4. 404页面【可选】 5. 文章 (三) 导航栏设置 1. 基础项 2. 菜单项 (四) 页面设置 1. 代码框设置 2. 社交图标设置 3.…

win10下wsl2使用记录(系统迁移到D盘、配置国内源、安装conda环境、配置pip源、安装pytorch-gpu环境、安装paddle-gpu环境)

wsl2 安装好后环境测试效果如下&#xff0c;支持命令nvidia-smi&#xff0c;不支持命令nvcc&#xff0c;usr/local目录下没有cuda文件夹。 系统迁移到非C盘 wsl安装的系统默认在c盘&#xff0c;为节省c盘空间进行迁移。 1、输出wsl -l 查看要迁移的系统名称 2、执行导出命…

JDK8 升级至JDK19

优质博文IT-BLOG-CN 目前部分项目使用JDK8&#xff0c;部分项目使用JDK19因此&#xff0c;环境变量中还是保持JDK8&#xff0c;只需要下载JDK19免安装版本&#xff0c;通过配置IDEA就可以完成本地开发。 一、IDEA 环境设置 【1】通过快捷键CTRL SHIFT ALT S或者File->P…

如何免费访问和使用Gemini API?

Gemini是谷歌开发的一个新模型。有了Gemini可以为查询提供图像、音频和文本&#xff0c;获得几乎完美的答案。 我们在本教程中将学习Gemini API以及如何在机器上设置它。我们还将探究各种Python API函数&#xff0c;包括文本生成和图像理解。 Gemini AI模型介绍 Gemini是谷歌…

两种方法判断Python的位数是32位还是64位

Python从1991年发布以来&#xff0c;凭借其简洁、清晰、易读的语法、丰富的标准库和第三方工具&#xff0c;在Web开发、自动化测试、人工智能、图形识别、机器学习等领域发展迅猛。     Python是一种胶水语言&#xff0c;通过Cython库与C/C语言进行链接&#xff0c;通过Jytho…

[Java][算法 滑动窗口]Day 03---LeetCode 热题 100---08~09

第一题 无重复字符串的最长子串 思路 其实就是在字符串S中 找到没有重复的最长子串的长度 这道题的难点就是在于如何判断最长并且无重复 首先 最长长度 可以使用变量max记录保存 再者 判断有无重复 最简单的方法就是 暴力遍历法 即对于每次找的子串都再次寻找遍历…

《辽宁春晚》开场动画惊艳亮相,蓝海创意云渲染服务再显神通

随着2024年甲辰龙年的脚步日益临近&#xff0c;备受瞩目的《辽宁春晚》于除夕夜为全国观众带来了一场精彩绝伦的视听盛宴。作为整场晚会的亮点之一&#xff0c;开场动画以其独特的创意和精美的画面效果&#xff0c;为观众带来了一个难忘的视觉体验。而这一精彩的呈现&#xff0…

测试物理网络的ping命令

通过发送Internet控制消息协议&#xff08;ICMP&#xff09;并接收其应答&#xff0c;测试验证与另一台TCP/IP计算机的IP级联通性、可达到性和名称解析的疑难问题主要TCP/IP命令。如果不带参数&#xff0c;ping将显示帮助。通过在命令提示符下输入“ping /&#xff1f;”命令&a…

R语言课程论文-飞机失事数据可视化分析

数据来源&#xff1a;Airplane Crashes Since 1908 (kaggle.com) 代码参考&#xff1a;Exploring historic Air Plane crash data | Kaggle 数据指标及其含义 指标名 含义 Date 事故发生日期(年-月-日) Time 当地时间&#xff0c;24小时制&#xff0c;格式为hh:mm Locat…

在客户端隔离的情况下通过 airtun-ng 实现直接客户端注入

直接的客户端注入技术 当我们试图执行一次无线攻击时&#xff0c;一个常见的问题就是&#xff0c;网络上的AP接入点拒绝在攻击者和被攻击者之间互转他们之间的攻击数据包。这种拒绝担任“中继”(relay)角色&#xff0c;而避免网络客户端之间互相攻击的技术&#xff0c;被称为“…

实例观察 c 语言中 volatile 的作用

volatile 意思是易变的。 在 c 语言中&#xff0c;如果变量被 volatile 修饰&#xff0c;就是告诉编译器这个变量随时都可能发生变化&#xff0c;那么每次读取变量的时候都会到内存中读取。 如果变量没有被 volatile 修饰&#xff0c;并且编译器发现在多次读取变量之间&#…

备战蓝桥杯---图论之建图基础

话不多说&#xff0c;直接看题&#xff1a; 首先&#xff0c;这个不是按照字典序的顺序&#xff0c;而是以只要1先做&#xff0c;在满足后让2先做。。。。 就是让数字小的放前面做拓扑排序。 我们可以先做1&#xff0c;看看它的前驱。 举个例子&#xff1a; 我们肯定要把1放…

JVM常见问题笔记分享

文章目录 1 JVM组成1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f;1.2 什么是程序计数器&#xff1f;1.3 你能给我详细的介绍Java堆吗?元空间(MetaSpace)介绍 1.4 什么是虚拟机栈1.5 堆和栈的区别1.6 能不能解释一下方法区&#xff1f;1.5.1 概述1.5.2 常量池1…

项目一:高并发内存池

1. 项目介绍 1.1 这个项目是做什么的 当前项目是实现一个高并发的内存池&#xff0c;他的原型是 google 的一个 开源项目tcmalloc &#xff0c; tcmalloc 全称 Thread-Caching Malloc &#xff0c;即线程缓存的 malloc &#xff0c;实现了高效的多线程内存管理&#xff0c;用…

蓝桥杯备赛_python_BFS搜索算法_刷题学习笔记

1 bfs广度优先搜索 1.1 是什么 1.2怎么实现 2案例学习 2.1.走迷宫 2.2.P1443 马的遍历 2.3. 九宫重排&#xff08;看答案学的&#xff0c;实在写不来&#xff09; 2.4.青蛙跳杯子&#xff08;学完九宫重排再做bingo&#xff09; 2.5. 长草 3.总结 1 bfs广度优先搜索 【P…

1.初识Tauri

文章目录 一、前言二、基本认识三、js与rust通信四、构建应用 一、前言 原文以及后续文章可点击查看&#xff1a;初识Tauri。 Tauri是一款比较新的跨平台桌面框架&#xff0c;也是我目前最喜欢的一个框架&#xff0c;其官网为&#xff1a;Tauri 它的作用其实和Electron很像&…

【PyQt】在PyQt5的界面上集成matplotlib绘制的图像

文章目录 0 前期教程1 概述2 matplotlib2.1 库导入2.2 图片的各个部分解释2.3 代码风格2.4 后端 3 集成matplotlib图像到pyqt界面中3.1 使用到的模块3.2 理解Qt Designer中的“控件提升”3.3 界面与逻辑分离的思路3.4 扩展 0 前期教程 【PyQt】PyQt5进阶——串口上位机及实时数…

transformer-Attention is All You Need(一)

1. 为什么需要transformer 循环模型通常沿输入和输出序列的符号位置进行因子计算。通过在计算期间将位置与步骤对齐&#xff0c;它们根据前一步的隐藏状态和输入产生位置的隐藏状态序列。这种固有的顺序特性阻止了训练样本内的并行化&#xff0c;这在较长的序列长度上变得至关重…

STM32-开发工具

开发过程中可能用到的工具 1、烧录下载调试工具ST-LINK ST-LINK&#xff0c;是ST(意法半导体)推出的调试编程工具&#xff0c;适用于STM32系列芯片的USB接口的下载及在线仿真器。 2、串口调试工具/串口下载工具 串口调试工具是一种用于通过串口通信协议与目标设备进行数据交…