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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据分析(一) 理解数据

1. 描述性统计&#xff08;summary&#xff09; 对于一个新数据集&#xff0c;首先通过观察来熟悉它&#xff0c;可以打印数据相关信息来大致观察数据的常规特点&#xff0c;比如数据规模&#xff08;行数列数&#xff09;、数据类型、类别数量&#xff08;变量数目、取值范围…

【STM32 物联网】基础AT指令与基础Wifi功能AT指令

文章目录 前言一、基础AT指令1.1 测试AT启动1.2 重启模块1.3 查看版本信息1.4 进入深度睡眠模式1.5 开关回显1.6 恢复出场设置1.7 UART配置设置临时设置&#xff0c;不保存到Flash设置串口保存到Flash 1.8 设置sleep模式查询当前sleep模式设置当前sleep模式 二、基础Wifi功能AT…

Mac录屏保存在哪?让你快速找到它们!

在日常生活和工作中&#xff0c;我们经常需要录制电脑屏幕&#xff0c;以便进行演示、教学或者保存重要信息。然而&#xff0c;对于许多苹果用户来说&#xff0c;他们可能并不清楚mac录屏文件在哪。在本文中&#xff0c;我们将详细介绍如何在mac上进行录屏&#xff0c;以及录屏…

网络原理(IP篇)

IP IPv4&#xff08;Internet Protocol&#xff09;数据报首部格式版本首部长度可变部分区分服务总长度标识、标志、片偏移生存时间协议首部检验和源IP地址和目的IP地址 NAT 机制&#xff08;**N**etwork **A**ddress **T**ranslator&#xff09;网络地址转换NAT将数据的转发由…

政安晨:【完全零基础】认知人工智能(二)【超级简单】的【机器学习神经网络】—— 底层算法

如果小伙伴第一次看到这篇文章&#xff0c;可以先浏览一下我这个系列的上一篇文章&#xff1a; 政安晨&#xff1a;【完全零基础】认知人工智能&#xff08;一&#xff09;【超级简单】的【机器学习神经网络】 —— 预测机https://blog.csdn.net/snowdenkeke/article/details/…

深度学习系列53:大模型微调概述

参考系列文章&#xff1a;https://zhuanlan.zhihu.com/p/635152813 github链接&#xff1a;https://github.com/liguodongiot/llm-action 1 训练范式 下面这种instructive learning&#xff0c;在模型参数达到80亿后&#xff0c;会发生质的提升&#xff1a; 类似的还有手写pr…

RK3568驱动指南|第一篇 驱动基础-第3章 helloworld 驱动实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

Vite 5.0 正式发布

11 月 16 日&#xff0c;Vite 5.0 正式发布&#xff0c;这是 Vite 道路上的又一个重要里程碑&#xff01;Vite 现在使用 Rollup 4&#xff0c;这已经代表了构建性能的大幅提升。此外&#xff0c;还有一些新的选项可以改善开发服务器性能。 Vite 4 发布于近一年前&#xff0c;它…

什么原因导致百度百科建立一直审核不通过?

百科词条对网络营销实在是太重要了&#xff0c;不管是个人还是企业想在网上开展业务&#xff0c;都必要建立百科词条。自己动手编辑百科词条&#xff0c;搞个几十次也审核不过的情况比比皆是。 为什么百度百科总是审核不通过&#xff1f;百度官方发表过声明表示百度百科词条是人…

骨传导耳机好用吗?如何挑选骨传导耳机?

骨传导耳机是一种非常创新的骨传导耳机&#xff0c;采用耳挂式佩戴&#xff0c;使用起来也非常舒适。 而且骨传导耳机最近几年还是比较火的&#xff0c;骨传导耳机的出现解决了传统入耳式耳机长时间佩戴不舒服、听力受损等问题。但随着骨传导耳机的品牌逐渐变多&#xff0c;很多…

Gin框架: 快速搭建起一个Web应用环境及处理不同类型的响应

Gin 框架简介 Gin是Golang应用最为广泛的框架之一Gin是轻量级http web框架&#xff0c;简易而高性能&#xff0c;专注于处理高并发场景 Gin框架环境搭建 基于已完成的Go环境, 参考 go mod 环境搭建 ↓ https://blog.csdn.net/Tyro_java/article/details/135297367 在初始化好…

【STM32 CubeMX】SPI HAL库编程

文章目录 前言一、CubeMX配置SPI Flash二、SPI HAL编程2.1 查询方式函数2.2 使用中断方式2.3 DMA方式 总结 前言 STM32 CubeMX 是一款由 STMicroelectronics 提供的图形化配置工具&#xff0c;用于生成 STM32 微控制器的初始化代码和项目框架。在 STM32 开发中&#xff0c;使用…

在Linux系统中设置HTTP隧道以实现网络穿透和端口转发

在数字化世界中&#xff0c;网络穿透和端口转发成为了许多开发者和系统管理员必备的技能。而在Linux系统中&#xff0c;通过设置HTTP隧道&#xff0c;我们可以轻松实现这一目标&#xff0c;让我们的服务即便在内网环境中也能被外部世界所访问。 那么&#xff0c;如何在Linux系…

idea 打不开项目 白屏

使用IDEA打开项目&#xff0c; 不知名原因崩溃了&#xff0c; 直接出现缩略图白屏。 解决过程&#xff1a; 尝试过重启IDEA&#xff0c;重启过电脑&#xff0c;重新引入相同项目&#xff08;使用不同路径&#xff0c;存在缓存记录&#xff0c;依然打不开&#xff09;&#xff…

磁盘删除的文件怎么恢复?4个简单方法(2024更新版)

“我有很多文件都保存在电脑磁盘里了&#xff0c;想问问电脑磁盘删除的文件有什么方法可以快速恢复吗&#xff1f;非常感谢大家&#xff01;” 在日常工作和生活中&#xff0c;我们经常会在磁盘中存储大量的文件。这些文件对我们来说或许都有特殊的意义。磁盘删除的文件怎么恢复…

C#,二进制数的按位旋转(Bits Rotate)算法与源代码

1 二进制数的按位旋转 二进制数的按位旋转&#xff08;翻转&#xff09;是编程中常见的按位运算方法。 二进制数的按位旋转分为左转、右转。 左转意味着数据变大&#xff0c;右转意味着数据变小&#xff08;有损&#xff09;。 2 源程序 using System; using System.Text; us…

TCP三次握手、四次挥手(简易版)

TCP是面向连接的&#xff1a;在真正通讯之前&#xff0c;必须先建立一条通讯线路&#xff0c;必须先完成连接。 TCP完成连接的过程&#xff1a;&#xff08;保证通讯线路畅通&#xff09; 建立连接&#xff1a; 三次握手基本过程 ①客户端首先向服务器发送一个建立连接的…

如何在亚马逊,美客多,阿里国际,速卖通上安全地进行自养号测评?

借鉴亚马逊等跨境电商市场的经验&#xff0c;我们建议选取具备以下特点的产品在平台上销售&#xff0c;以实现需求稳定、竞争较低、利润较高&#xff0c;同时规避法律纠纷和质检问题。首先&#xff0c;应选择体积小、重量轻、易于运输的商品&#xff0c;这有助于降低运输成本和…

Java 2:运算符、表达式和语句

2.1 运算符与表达式 Java提供了丰富的运算符&#xff0c;如算术运算符、关系运算符、逻辑运算符、位运算符等。Java语言中的绝大多数运算符和C语言相同&#xff0c;基本语句如条件分支语句&#xff0c;循环语句等&#xff0c;也和C语言类似。 2.1.1算术运算符与算术表达式 1…

vue打包优化,webpack的8大配置方案

vue-cli 生成的项目通常集成Webpack &#xff0c;在打包的时候&#xff0c;需要webpack来做一些事情。这里我们希望它可以压缩代码体积&#xff0c;提高运行效率。 文章目录 &#xff08;1&#xff09;代码压缩&#xff1a;&#xff08;2&#xff09;图片压缩&#xff1a;&…