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

news2025/2/15 8:29:00

一、介绍

在多线程编程中,确保对共享变量进行原子操作是至关重要的。当多个线程同时访问和修改同一共享资源时,如果没有合适的同步机制,可能会导致数据竞争、内存一致性问题,甚至造成程序崩溃。为了解决这个问题,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.coloradmin.cn/o/1454095.html

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

相关文章

【退役之重学前端】JavaScript 类、构造器、原型的关系

ES6中类的概念&#xff0c;我之前花了较长的时间学习Java&#xff0c;所以对类感觉很亲切。我并不满足仅仅会使用&#xff0c;让我们一起深究一下 JavaScript 中的类吧。 构造一个类&#xff0c;并实例化一个对象 class Animal{constructor(name){this.name name;}getName(){…

中国传媒网CEO徐晓艺:媒体融合发展业态新媒体年后在沪召开

近日,在“坚守媒体初心,拥抱AI时代”2023外滩新媒体年会上,有多项合作达成。 在当前竞争激烈的市场环境中,媒体宣传已经成为企业品牌推广不可或缺的一环。对于很多企业来说往往会犯一个错误,就是默默地参加了展会,并没有进行媒体营销。展会是一种非常有力的宣传和推广方式,可以…

Vue中 如何监听键盘事件中的按键

在Web前端开发中&#xff0c;键盘事件的处理是非常常见的需求之一。而在Vue框架中&#xff0c;如何监听键盘事件中的按键是一个相对简单但又很实用的功能。本文将为你介绍如何在Vue中监听键盘事件&#xff0c;并演示一些常用的按键操作。 首先&#xff0c;在Vue中监听键盘事件…

数学建模【线性规划】

一、线性规划简介 线性规划通俗讲就是“有限的资源中获取最大的收益”&#xff08;优化类问题&#xff09;。而且所有的变量关系式都是线性的&#xff0c;不存在x、指数函数、对数函数、反比例函数、三角函数等。此模型要优化的就是在一组线性约束条件下&#xff0c;求线性目标…

2024年全国教资笔试报名流程(建议电脑报名),看看有啥新要求?

一.报名、考试时间节点 1.笔试报名时间: 2024年1月12日-15日 2.笔试考试时间:2024年3月9日 3.笔试成绩查询时间:2024年4月15日 4.面试报名时间:2024年4月15日 5.面试考试时间:2024年5月18日 6.面试成绩查询时间:2024年6月14日 二.笔试报名流程: 登陆→考生注册 →填报个…

ArcGIS学习(八)基于GIS平台的控规编制办法

ArcGIS学习(八)基于GIS平台的控规编制办法 上一任务我们学习了”如何进行图片数据的矢量化?" 这一关我们来学习一个比较简单的案例一一”如何在ArcGIS中录入控规指标,绘制控规图纸?" 首先,先来看看这个案例的分析思路以及导入CAD格式的控规图纸。 接着,来看…

LeetCode周赛384 题解

AK 第 384 场周赛 - 力扣&#xff08;LeetCode&#xff09; 前两题都是签到, 略。 第三题: 回文字符串的最大数量 1、题意: 给定一个字符串数组&#xff0c;总字符数量不超过1e6, 可以交换其中的任意两个字符&#xff0c;问能构造最多几个回文字符串。 2、题解: 首先我…

中科大计网学习记录笔记(十二):TCP 套接字编程

前前言&#xff1a;大家看到这一章节的时候一定不要跳过&#xff0c;虽然标题是编程&#xff0c;但实际上是对 socket 的运行机制做了详细的讨论&#xff0c;对理解 TCP 有很大的帮助&#xff1b;但是由于本节涉及到了大量的编程知识&#xff0c;对于一些朋友来说不是很好理解&…

阿里云香港服务器cn2速度测试和租用价格表

阿里云香港服务器中国香港数据中心网络线路类型BGP多线精品&#xff0c;中国电信CN2高速网络高质量、大规格BGP带宽&#xff0c;运营商精品公网直连中国内地&#xff0c;时延更低&#xff0c;优化海外回中国内地流量的公网线路&#xff0c;可以提高国际业务访问质量。阿里云服务…

『运维备忘录』之 CMD 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

Linux-系统资源管理的命令

目录 查看CPU&#xff1a;more /proc/meminfo 查看内存数据&#xff1a;free -m / free -h 查看系统版本&#xff1a;more /etc/issue 查看操作系统的类型&#xff1a;uname -a 查看主机名称&#xff1a;hostname 查看磁盘空间&#xff1a;df -h 查看某个目录空间…

互联网加竞赛 基于设深度学习的人脸性别年龄识别系统

文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习机器视觉的…

【IEEE-Trans】这本TOP刊一点不拖泥带水,审稿很利索!质量也很不错!

【SciencePub学术】 期刊信息简介 IEEE TRANSACTIONS ON INSTRUMENTATION AND MEASUREMENT IF(2022)&#xff1a;5.6&#xff0c;JCR1区&#xff0c;中科院2区TOP 期刊数据指标 ISSN&#xff1a;0018-9456 IF(2022)&#xff1a;5.6 自引率&#xff1a;23.20% 年发文量&a…

好的程序员不该局限技术故步自封,更多去了解产品,运营,销售,推广,公司运作吧

在当今技术迅速发展的时代&#xff0c;作为程序员&#xff0c;我们常常面临着学习速度跟不上技术变化的困扰。每年涌现的新技术、新框架&#xff0c;给我们带来了巨大的挑战。尤其是随着年龄增长&#xff0c;学习能力的下降似乎让我们更加被动。技术的发展也并非一帆风顺&#…

【大厂AI课学习笔记】【2.1 人工智能项目开发规划与目标】(6)特征工程初步

特征工程是一个非常重要的概念&#xff0c;从特征工程可以领会到机器学习的真谛。 特征工程就是从原始数据转换为特征向量的过程。 特征工程的特点&#xff1a; 特征工程是机器学习中很重要的起始步骤&#xff0c;直接影响效果&#xff0c;需要大量的时间。 数据和特征决定了…

NLP深入学习:《A Survey of Large Language Models》详细学习(六)

文章目录 1. 前言2. LLMs 能力与评价方法2.1 基础能力2.1.1 语言生成2.1.2 知识利用率2.1.3 复杂推理 2.2 高阶能力2.3 基准和评估方法2.3.1 评价基准2.3.2 评估方法2.3.3 评估方法优点和不足 3. 参考 1. 前言 最近正在读 LLM 论文的综述&#xff0c;当前采取的策略是部分内容…

【JVM】打破双亲委派机制

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;JVM ⛺️稳中求进&#xff0c;晒太阳 打破双亲委派机制 打破双亲委派机制三种方法 自定义类加载器 ClassLoader包含了四个核心方法 //由类加载器子类实现&#xff0c;获取二进制数据调用…

安装cockpit

1、下载cockpit yum -y install cockpit 下载相关环境 yum install qemu-kvm libvirt libvirt-daemon virt-install virt-manager libvirt-dbus 2、启动libvirtd systemctl start libvirtd.service systemctl enable libvirtd.service 3、设置开机自启动 systemctl enabl…

高程 | 继承与派生(c++)

文章目录 &#x1f4da;继承的概念和语法&#x1f4da;派生类生成过程&#x1f4da;继承权限和继承方式&#x1f407;公有继承&#x1f407;私有继承&#x1f407;保护继承 &#x1f4da;类型转换规则&#x1f4da;派生类构造函数和析构函数&#x1f4da;继承中的静态成员特性&…

并发编程之深入理解JVM并发三大特性

并发编程之深入理解JVM&并发三大特性 并发编程解决的问题 ​ 多线程同步&#xff08;一个线程需要等待另一个线程的结果&#xff0c;一个线程依赖于另一个线程&#xff09;&#xff0c;互斥&#xff08;一个资源只能一个线程使用&#xff09;&#xff0c;分工&#xff08…