【问题标题】:memory location and data race in cc中的内存位置和数据竞争
【发布时间】:2013-11-04 04:35:32
【问题描述】:

http://www.stroustrup.com/C++11FAQ.html#memory-model

页面描述:

// thread 1:
char c;
c = 1;
int x = c;

// thread 2:
char b;
b = 1;
int y = b;

.....

但是,大多数现代处理器不能读取或写入单个字符,它必须读取或写入整个单词,因此对 c 的赋值实际上是``读取包含 c 的单词,替换 c 部分,然后写入再次返回单词。'' 由于对 b 的赋值是相似的,因此两个线程有​​很多机会相互破坏,即使线程不(根据它们的源文本)共享数据!

我有一个结构:

struct data_
{
    volatile char c ;
    volatile char b ;
}  __attribute__(( aligned(2) ));
typedef struct data_ data ;

和全局变量:

data dx ; 
int x,y ;

线程1:

    dx.c = 1 ;
    x = dx.c ;

线程2:

    dx.b = 1 ;
    y = dx.b ;

在 gcc 4.4.6 编译,运行 1,000,000 次, 看起来我没有得到任何价值,不是 (x==1 && y==1) !!!!

struct data_
{
    volatile char c ;
    volatile char b ;
}  __attribute__(( aligned(2) ));

我故意将 char c 和 char b 放在一个对齐的结构中(2),以便它们都属于 同一个词,根据网页描述,我可能有机会 得到结果不是 (x==1 && y==1) ,事实是运行测试 1,000,000 次, all get (x==1 && y==1) ,是因为 gcc 有什么技巧吗?还是我错过了什么?

编辑:

线程1:

int ilocal ;
while(1)
{
    sem_wait(sem1) ;
    dx.c = 1 ;
    x = dx.c ;
    ilocal = __sync_add_and_fetch(&icnt,1) ;
    if(ilocal == 2)
        sem_post(sem3) ;
    ++icnt1 ;
}

线程2:

int ilocal ;
while(1)
{
    sem_wait(sem2) ;
    dx.b = 1 ;
    y = dx.b ;
    ilocal = __sync_add_and_fetch(&icnt,1) ;
    if(ilocal == 2)
        sem_post(sem3) ;
    ++icnt2 ;
}

主要:

int idx,iflag1=0,iflag2=0 ;
for(idx=0;idx<1000000;idx++)
{
    icnt = 0 ; dx.c=0 ; dx.b=0 ;
    sem_post(sem1) ;
    sem_post(sem2) ;
    sem_wait(sem3) ;
    if( ! ((x==1)&&(y==1))  )
    {
        printf("result that (x==%d && y==%d) \n",x,y) ;
        ++iflag1 ;
    }else{
        ++iflag2 ;
    }
} //while
printf("iflag1=(%d),iflag2=(%d)\n",iflag1,iflag2) ;
printf("icnt1=(%d),icnt2=(%d) \n",icnt1,icnt2) ;

gcc memorylocate.c -lpthread -o memorylocate.exe

sizeof data=(64) //source already change to __attribute__(( aligned(64) )
iflag1=(0),iflag2=(1000000)
icnt1=(1000000),icnt2=(1000000)

编辑2:

我想我终于弄明白了!!

struct { char c ; char b ;}

c 和 b 将是不同的内存位置,因此它们可以是线程安全的访问! cpu可以原子访问单字节字符!!!!!!

我将代码更改为:

struct data_
{
    unsigned char c:4 ;
    unsigned char b:4 ;
} ;

主要是:

for(idx=0;idx<1000000;idx++)
{
    icnt = 0 ; dx.c=0 ; dx.b=0 ;
    sem_post(sem1) ;
    sem_post(sem2) ;
    sem_wait(sem3) ;
    if( ! ((dx.c==1)&&(dx.b==1))  )
    {
        printf("result that (x==%d && y==%d) \n",x,y) ;
        ++iflag1 ;
    }else{
        ++iflag2 ;
    }
} //while

我观察到结果不是 (dx.c==1)&&(dx.b==1) !!这是因为在这种情况下 dx.c 和 dx.b 在同一个内存位置!!!!

所以我犯了一个错误,最重要的是要决定内存位置,struct {char c; char b;} , char c 和 char b 在不同的内存位置,测试结果是正确的!!!!

【问题讨论】:

  • 这不仅仅是它们是否在同一个单词中,还要根据您的缓存大小来确保它们实际上也在同一个缓存行中。
  • @kfsone,thanks,change to aligned(64) ,仍然得到相同的结果,你的意思是这只是一个虚假的分享问题?
  • 您是否有将dx.bdx.c 更改为1 以外的值的代码?
  • @greatwolf ,是的,在主线程中设置 dx.b 和 dx.c 都为 0 ,并且信号量发布 thread1 和 thread2 运行,并从主线程获取结果,一直得到(x==1 && y==1)
  • @greatwolf ,我已经发布了 main 和 thread1,thread2 代码..

标签: c++ c multithreading gcc


【解决方案1】:

请参阅 C++ 标准中的 1.7 [intro.memory] ​​p3:

内存位置要么是标量类型的对象,要么是最大值 相邻位域的序列都具有非零宽度。 [ 笔记: ... ] 两个或多个执行线程(1.10)可以更新和访问 独立的内存位置,互不干扰。

因此,符合标准的编译器只需确保可以更新 char 而不会影响其他内存位置。

【讨论】:

  • ...除非您使用非标准扩展来弄乱对齐方式,在这种情况下,您必须查阅编译器手册才能确定。
猜你喜欢
  • 1970-01-01
  • 2013-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-27
  • 1970-01-01
相关资源
最近更新 更多