Interlocked原子函数陷阱
一、问题
windows api函数中提供了InterlockedExchange、InterlockedDecrement, InterlockedIncrement, ExInterlockedAddLargeInteger, ExInterlockedAddUlong等原子访问函数,在众多线程同步方法中效率最高。
最近在工作中,在类A中添加了两个变量作为标志位,用于多线程间的标志同步用,所以用到了InterlockedExchange函数,类似如下代码:
//.h文件
class A{
public:
void SetFirstFlag(const bool &flag);//设置标志位a
bool GetFirstFlag();//获取标志a的值 void SetSecondFlag(const bool& flag);//设置标志位b
bool GetSecondFlag();//获取标志b的值
private:
bool a;
bool b;
}; //.cpp文件
A::A(void){
a = true;
b = true;
}
A::~A(void){ } void A::SetFirstFlag(const bool& flag){//设置标志位a
InterlockedExchange((unsigned long*)&a, flag);
} bool A::GetFirstFlag(){//获取标志a的值
if (InterlockedExchange((unsigned long*)&a, false) == true)
{
SetFirstFlag(true);//恢复为原来的值
return true;
} return false;
}
void A::SetSecondFlag(const bool& flag){//设置标志位b
InterlockedExchange((unsigned long*)&b, flag);
} bool A::GetSecondFlag(){//获取标志b的值
if (InterlockedExchange((unsigned long*)&b, false) == true)
{
SetSecondFlag(true);//恢复为原来的值
return true;
} return false;
}
这个类实现的功能很简单,调用SetFirstdFlag或者SetSecondFlag用于设置a或者b的值,调用GetFirstFlag或者GetSecondFlag获取a或者b的值。
但是在实际调试过程中发现,明明在构造函数函数中设置了a为true,但是在第一次进入到相应的SetSecondFlag或者GetSecondFlag函数中时,却发现b的值为false,而且完全不受控制,一会儿true一会儿为false,搞的我都怀疑人生了。
在耽误了一下午后,发现问题出在了指针操作上。在win32下,bool型占用1字节数据,a和b在内存上相邻,各占用1字节,在类A调用构造函数初始化后,内存内容是:
a和b的值都是1,但是这里函数InterlockedExchange的实际定义是:
FORCEINLINE
unsigned long
InterlockedExchange(
__inout __drv_interlocked unsigned long volatile *Target,
__in unsigned long Value
)
{
return (unsigned long) InterlockedExchange((volatile long*) Target, (long) Value);
}
很明显,被改变的变量是以long指针操作修改值的,所以每次调用InterlockedExchange((unsigned long*)&a, false)时,是将a的值赋值给了a的地址指向的4字节内存,所以b的值也被篡改了,但是修改b的值不会影响a的值。
到这里,很明显,是调用api函数的时候没注意传入参数的含义,也没注意数据类型占用的字节数导致该问题。将类A中将所有bool修改为BOOL后,因为BOOL和long都是占用4字节内存,问题就得以解决了。
二、总结
在调用api函数的时候一定要注意参数含义,尤其是指针操作的时候,要注意参数占用字节数,调用方式错误,轻则导致数据逻辑错误,重则软件崩溃。
Interlocked原子函数陷阱的更多相关文章
- 读书笔记——Windows核心编程(8)Interlocked系列函数
先让我们来复习下小学知识 A+B=C//式中A为被加数,B为加数. A-B=C//式中A为被减数,B为减数. 再让我们来明确一个知识点:返回值为void的Windows函数意味着一定会执行成功. -- ...
- 原子操作 Interlocked系列函数
上一篇<多线程第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是 ...
- (转)原子操作 Interlocked系列函数
上一篇<多线程第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是 ...
- 多线程笔记--原子操作Interlocked系列函数
前面写了一个多线程报数的功能,为了描述方便和代码简洁起见,只输出最后的报数结果来观察程序运行结果.这非常类似一个网站的客户访问统计,每个用户登录用一个线程模拟,线程运行时将一个表示计数的变量递增.程序 ...
- 多线程面试题系列(3):原子操作 Interlocked系列函数
上一篇中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是否运行出错.这也非常类似于统计一个网站每天有多少用户登录,每个用户登录用一个线程模拟,线程运行时会将 ...
- [Go] golang原子函数锁住共享资源
1.atomic包里的几个函数以及sync包里的mutex类型,提供了解决方案2.原子函数能够以很底层的加锁机制来同步访问整型变量和指针3.atomic.AddInt64(&counter, ...
- 秒杀多线程第三篇 原子操作 Interlocked系列函数
上一篇<多线程第一次亲密接触 CreateThread与_beginthreadex本质区别>中讲到一个多线程报数功能.为了描述方便和代码简洁起见,我们可以只输出最后的报数结果来观察程序是 ...
- [OS] 多线程--原子操作 Interlocked系列函数
转自:http://blog.csdn.net/morewindows/article/details/7429155 上一篇<多线程--第一次亲密接触 CreateThread与_begint ...
- 多线程--原子操作 Interlocked系列函数
[转]原文地址:http://blog.csdn.net/morewindows/article/details/7429155 线程同步与互斥: 互斥主要指多个线程不能同时访问一个资源,如打印机就是 ...
随机推荐
- grok 匹配log4j
input { file { codec => multiline { pattern => "^\[2016" negate => true what => ...
- 查看c语言的函数运行时间的简单模板
/* clock():捕捉从程序开始运行到 clock() 被调用时所耗费的时间,时间单位 是clock tick, 即:"时钟打点". 常数CLK_TCK:机器时钟每秒所走的时钟 ...
- JavaScript:window.onload问题
前几天做一个点击按钮,就实现切换图片效果的小demo时,代码看上去没问题,就是达不到效果.让我百思不得其解. 代码如下: <!DOCTYPE html> <html> < ...
- java设计模式之 装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构. 这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装 ...
- 关于JQuery获取宽度和高度在chrome和IE下的不同
之前写了一个关于滚动条的东西,可是在写的时候发现JQuery在获取宽度和高度时在不同浏览器中是不一样的,下面发一下代码给给位看官先展示一下: $(function(){ $("#main&q ...
- javaCV开发详解之6:本地音频(话筒设备)和视频(摄像头)抓取、混合并推送(录制)到服务器(本地)
javaCV系列文章: javacv开发详解之1:调用本机摄像头视频 javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG.j ...
- ES6 常用总结——第一章(简介、let、const)
ES6整理 1. ECMAScript 6简介 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准.它的目标,是使得 JavaScript 语言可以用来编写复杂 ...
- Github 开源:高效好用的对象间属性拷贝工具:升讯威 Mapper( Sheng.Mapper)
Github 地址:https://github.com/iccb1013/Sheng.Mapper 对象属性值映射/拷贝工具.不需要创建映射规则,不要求对象类型一致,适用于简单直接的拷贝操作,可以全 ...
- 工作流调度引擎---Oozie
Oozie使用教程 一. Oozie简介 Apache Oozie是用于Hadoop平台的一种工作流调度引擎. 作用 - 统一调度hadoop系统中常见的mr任务启动hdfs操作.shell调度. ...
- 备份Rhythmbox播放器的曲目和播放列表信息
Rhythmbox音乐播放器只能保存单个播放列表,如果在rhythmbox下建了很多播放列表(比如按歌手名分类),每个播放列表下包含一些歌曲,为了避免重装系统后重新建这些播放列表,可以备份下面的文件. ...