InterLockedIncrement and InterLockedDecrement

本文通过详细解释InterlockedIncrement和InterlockedDecrement函数的工作原理,展示了如何利用这些函数在多线程环境下确保类的初始化和注销计算仅执行一次,从而实现单元代码的封装性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

InterLockedIncrement and InterLockedDecrement
实现数的原子性加减。什么是原子性的加减呢?
举个例子:如果一个变量 Long value =0;
首先说一下正常情况下的加减操作:value+=1;
1:系统从Value的空间取出值,并动态生成一个空间来存储取出来的值;
2:将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束。


如果此时有两个Thread ,分别记作threadA,threadB。
1:threadA将Value从存储空间取出,为0;
2:threadB将Value从存储空间取出,为0;
3:threadA将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=1。
4:threadB将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=1。
最后Value =1 ,而正确应该是2;这就是问题的所在,InterLockedIncrement 能够保证在一个线程访问变量时其它线程不能访问。同理InterLockedDecrement。
LONG   InterlockedDecrement(   
      LPLONG   lpAddend       //   variable   address   
);   
属于互锁函数,用在同一进程内,需要对共享的一个变量,做减法的时候,   
防止其他线程访问这个变量,是实现线程同步的一种办法(互锁函数) 
   
首先要理解多线程同步,共享资源(同时访问全局变量的问题),否则就难以理解。   
     
result   =   InterlockedDecrement(&SomeInt)   
     
如果不考虑多线程其实就是   result   =   SomeInt   -   1;   
     
但是考虑到多线程问题就复杂了一些。就是说如果想要得到我预期的结果并不容易。   
     
result   =   SomeInt   -   1;   
     
举例说:   
SomeInt如果==1;   
预期的结果result当然==0;   
     
但是,如果SomeInt是一个全程共享的全局变量情况就不一样了。   
C语言的"result   =   SomeInt   -   1;"   
在实际的执行过程中,有好几条指令,在指令执行过程中,其它线程可能改变SomeInt值,使真正的结果与你预期的不一致。   
     
所以InterlockedDecrement(&SomeInt)的执行过程是这样的   
{   
      __禁止其他线程访问   (&SomeInt)   这个地址   
     
      SomeInt   --;   
         
      move   EAX,   someInt;   //   设定返回值,C++函数的返回值   都放在EAX中,   
     
      __开放其他线程访问   (&SomeInt)   这个地址   
}   
     
但是实际上只需要几条指令加前缀就可以完成,以上说明是放大的。   
     
你也许会说,这有必要吗?   一般来说,发生错误的概率不大,但是防范总是必要的 
如果不考虑多线程   
result   =   InterlockedDecrement(&SomeInt);   
就是result   =   SomeInt   -   1;   
如果SomeInt==1,result一定==0;   
     
但是,在多线程中如果SomeInt是线程间共享的全局变量,情况就不那么简单了。   
result   =   SomeInt   -   1;   
在CPU中,要执行好几条指令。在指令中间有可能SomeInt被线程修改。那实际的结果就不是你预期的结果了。   
     
InterlockedDecrement(&SomeInt)   
放大的过程,如下:   
{   
      __禁止其他线程访问   &SomeInt   地址;   
     
      SomeInt   --;   
         
      /////其他线程不会在这里修改SomeInt值。   !!!!!!   
     
      mov   EAX,   SomeInt;   //C++   函数返回值   总放在EAX中。   
         
      __开放其他线程访问   &SomeInt   地址;   
}   
     
实际的CPU执行过程只有几条加前缀的指令(586指令)   
     

你会说,有必要吗?   出错的概率不大,但是错误总是需要防范的。当然可以用其他多线程机制实现,但是都没有这样简洁,所以Interlocked...函数有必要提供。

补充知识类似的还有下面的几个
(1) LONG InterlockedExchangeAdd ( LPLONG Addend, LONG Increment );
Addend为长整型变量的地址,Increment为想要在Addend指向的长整型变量上增加的数值(可以是负数)。这个函数的主要作用是保证这个加操作为一个原子访问。
(2) LONG InterlockedExchange( LPLONG Target, LONG Value );
用第二个参数的值取代第一个参数指向的值。函数返回值为原始值。
(3) PVOID InterlockedExchangePointer( PVOID *Target, PVOID Value );
用第二个参数的值取代第一个参数指向的值。函数返回值为原始值。
(4) LONG InterlockedCompareExchange( 
LPLONG Destination, LONG Exchange, LONG Comperand );
如果第三个参数与第一个参数指向的值相同,那么用第二个参数取代第一个参数指向的值,否则Destination就不改变,函数返回值为原始值。
(5) PVOID InterlockedCompareExchangePointer (
PVOID *Destination, PVOID Exchange, PVOID Comperand );
如果第三个参数与第一个参数指向的值相同,那么用第二个参数取代第一个参数指向的值。,否则Destination就不改变,函数返回值为原始值。


一般来说,在多用户线程环境中,我们使用临界区、事件对象甚至互斥量来进行同步,尤其是临界区,可以很方便地对某些变量甚至代码块进行锁定执行,防止多线程之间资源恶性抢夺。既然如此,为啥微软又单独提供了专用于一个数值锁定计算的API函数InterlockedIncrement和InterlockedDecrement呢?他们又有什么特殊作用呢?
恰好近段时间写了一个这方面的应用,帮我加深了对这类API函数的理解。
首先描述一下需求,在应用中,有这样一个类,它可能只被实例化一次,也可能会被实例化多次,但不管被实例化了几次,它必须在构造函数里执行一项初始化计算,假设初始化计算的函数为WSAStartup,同时还需要在析构函数里执行一下注销计算,假设注销计算的函数为WSACleanup,现在有一个要求,就是它们的初始化和注销计算只能被执行一次,就如同在一个项目中,只能运行一次WSAStartup和WSACleanup一样。当然,大家可能会想到直接在工程的开始和结尾处实现这样的功能,但是,如果把这个类的文件包括在其它测试工程里进行测试,同时不改变其它工程的代码,又该如何实现呢?
 其实,我们可以充分利用InterlockedIncrement和InterlockedDecrement,就如同COM的CoInitialize()和CoUninitialize()一样,描述代码如下:
 假设类名为A:
 view plaincopy to clipboardprint?
class A 

    protected: 
    static long m_nRef; 
   public: 
  / /类A的构造函数 
  A() 
  { 
      if(1 == InterlockedIncrement(&m_nRef)) 
  { 
  //以下代码只执行一次 
   WSADATA wsaData; 
   WSAStartup(MAKEWORD(2,2), &wsaData); 
   } 
};
  //类A的虚析构函数 
   virtual ~A() 
  { 
      if(0 == InterlockedDecrement(&m_nRef)) 
  { 
  //以下代码只执行一次 
   WSACleanup(); 
  } 
   } 
  }; 
   long A::m_nRef = 0; 
  class A
 {
       protected:
       static long m_nRef;
      public:
      //类A的构造函数
    A()
    {
      if(1 == InterlockedIncrement(&m_nRef))
     {
        //以下代码只执行一次
       WSADATA wsaData;
       WSAStartup(MAKEWORD(2,2), &wsaData);
    }
 };
 //类A的虚析构函数
 virtual ~A()
 {
      if(0 == InterlockedDecrement(&m_nRef))
      {
     //以下代码只执行一次
      WSACleanup();
      }
    }
};
long A::m_nRef = 0;
 
这样,无论我们创建了类A的多少个实例,在类的构造函数和析构函数里,WSAStartup和WSACleanup均只被执行一次,有效地保证了单元代码的封装性。
C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9878): error C2065: “InterlockedIncrement64”: 未声明的标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9910): error C2065: “InterlockedDecrement64”: 未声明的标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9946): error C3861: “InterlockedExchange64”: 找不到标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(10000): error C3861: “InterlockedExchangeAdd64”: 找不到标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(10010): error C3861: “InterlockedExchangeAdd64”: 找不到标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(10057): error C3861: “InterlockedAnd64”: 找不到标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(10067): error C3861: “InterlockedOr64”: 找不到标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(10077): error C3861: “InterlockedXor64”: 找不到标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\winrt\wrl\implements.h(1570): error C3861: “InterlockedCompareExchangeNoFence”: 找不到标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\winrt\wrl\implements.h(1588): error C3861: “InterlockedCompareExchangeRelease”: 找不到标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\winrt\wrl\implements.h(1874): error C2065: “_ARM64_BARRIER_ISH”: 未声明的标识符 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\winrt\wrl\implements.h(1874): error C3861: “__dmb”: 找不到标识符 src\Candidate\EnumTfCandidates.cpp(78): error C2665: “InterlockedIncrement”: 没有重载函数可以转换所有参数类型 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9874): note: 可能是“unsigned __int64 InterlockedIncrement(volatile unsigned __int64 *)” src\Candidate\EnumTfCandidates.cpp(78): note: “unsigned __int64 InterlockedIncrement(volatile unsigned __int64 *)”: 无法将参数 1 从“LONG *”转换为“volatile unsigned __int64 *” src\Candidate\EnumTfCandidates.cpp(78): note: 指向的类型不相关; 转换需要 reinterpret_cast、C 样式强制转换或带圆括号的函数样式强制转换 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9862): note: 或 “unsigned long InterlockedIncrement(volatile unsigned long *)” src\Candidate\EnumTfCandidates.cpp(78): note: “unsigned long InterlockedIncrement(volatile unsigned long *)”: 无法将参数 1 从“LONG *”转换为“volatile unsigned long *” src\Candidate\EnumTfCandidates.cpp(78): note: 指向的类型不相关; 转换需要 reinterpret_cast、C 样式强制转换或带圆括号的函数样式强制转换 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9853): note: 或 “unsigned int InterlockedIncrement(volatile unsigned int *)” src\Candidate\EnumTfCandidates.cpp(78): note: “unsigned int InterlockedIncrement(volatile unsigned int *)”: 无法将参数 1 从“LONG *”转换为“volatile unsigned int *” src\Candidate\EnumTfCandidates.cpp(78): note: 指向的类型不相关; 转换需要 reinterpret_cast、C 样式强制转换或带圆括号的函数样式强制转换 src\Candidate\EnumTfCandidates.cpp(78): note: 尝试匹配参数列表“(LONG *)”时 src\Candidate\EnumTfCandidates.cpp(83): error C2665: “InterlockedDecrement”: 没有重载函数可以转换所有参数类型 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9906): note: 可能是“unsigned __int64 InterlockedDecrement(volatile unsigned __int64 *)” src\Candidate\EnumTfCandidates.cpp(83): note: “unsigned __int64 InterlockedDecrement(volatile unsigned __int64 *)”: 无法将参数 1 从“LONG *”转换为“volatile unsigned __int64 *” src\Candidate\EnumTfCandidates.cpp(83): note: 指向的类型不相关; 转换需要 reinterpret_cast、C 样式强制转换或带圆括号的函数样式强制转换 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9894): note: 或 “unsigned long InterlockedDecrement(volatile unsigned long *)” src\Candidate\EnumTfCandidates.cpp(83): note: “unsigned long InterlockedDecrement(volatile unsigned long *)”: 无法将参数 1 从“LONG *”转换为“volatile unsigned long *” src\Candidate\EnumTfCandidates.cpp(83): note: 指向的类型不相关; 转换需要 reinterpret_cast、C 样式强制转换或带圆括号的函数样式强制转换 C:\Program Files (x86)\Windows Kits\10\\include\10.0.26100.0\\um\winbase.h(9885): note: 或 “unsigned int InterlockedDecrement(volatile unsigned int *)” src\Candidate\EnumTfCandidates.cpp(83): note: “unsigned int InterlockedDecrement(volatile unsigned int *)”: 无法将参数 1 从“LONG *”转换为“volatile unsigned int *” src\Candidate\EnumTfCandidates.cpp(83): note: 指向的类型不相关; 转换需要 reinterpret_cast、C 样式强制转换或带圆括号的函数样式强制转换 src\Candidate\EnumTfCandidates.cpp(83): note: 尝试匹配参数列表“(LONG *)”时 > in src\Candidate\EnumTfCandidates.cpp PS D:\Downloads\MetasequoiaImeTsf-master\MetasequoiaImeTsf - 副本>
06-17
电动汽车数据集:2025年3K+记录 真实电动汽车数据:特斯拉、宝马、日产车型,含2025年电池规格和销售数据 关于数据集 电动汽车数据集 这个合成数据集包含许多品牌和年份的电动汽车和插电式车型的记录,捕捉技术规格、性能、定价、制造来源、销售和安全相关属性。每一行代表由vehicle_ID标识的唯一车辆列表。 关键特性 覆盖范围:全球制造商和车型组合,包括纯电动汽车和插电式混合动力汽车。 范围:电池化学成分、容量、续航里程、充电标准和速度、价格、产地、自主水平、排放、安全等级、销售和保修。 时间跨度:模型跨度多年(包括传统和即将推出的)。 数据质量说明: 某些行可能缺少某些字段(空白)。 几个分类字段包含不同的、特定于供应商的(例如,Charging_Type、Battery_Type)。 各列中的单位混合在一起;注意kWh、km、hr、USD、g/km和额定。 列 列类型描述示例 Vehicle_ID整数每个车辆记录的唯一标识符。1 制造商分类汽车品牌或OEM。特斯拉 型号类别特定型号名称/变体。型号Y 与记录关联的年份整数模型。2024 电池_类型分类使用的电池化学/技术。磷酸铁锂 Battery_Capacity_kWh浮充电池标称容量,单位为千瓦时。75.0 Range_km整数表示充满电后的行驶里程(公里)。505 充电类型主要充电接口或功能。CCS、NACS、CHAdeMO、DCFC、V2G、V2H、V2L Charge_Time_hr浮动充电的大致时间(小时),上下文因充电方法而异。7.5 价格_USD浮动参考车辆价格(美元).85000.00 颜色类别主要外观颜色或饰面。午夜黑 制造国_制造类别车辆制造/组装的国家。美国 Autonomous_Level浮点自动化能力级别(例如0-5),可能包括子级别的小
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值