2020-03-12

1. MEMORY_BARRIER的正确用法

为了更好的说明问题,这里只讨论读写内存栅栏,关于读内存栅栏、写内存栅栏可以看我发的文档。 以下为简化的伪代码,这是唯一正确的MEMORY_BARRIER用法:

//生产者(对应原来的PUSH接口):
PUT(data)
{
    if (writeCusor + 1 != readCusor)
    {
        dataBuffer[writeCusor] = data;
        MEMORY_BARRIER();
        writeCusor++;
    }
}

//消费者(对应原来的POP接口):
GET(data)
{
    if (readCusor != writeCusor)
    {
        data = dataBuffer[readCusor];
        MEMORY_BARRIER();
        readCusor++;
    }
}

2. MEMORY_BARRIER的语义

MEMORY_BARRIER原语的语义为: 保证MEMORY_BARRIER之前的所有读写操作都在MEMORY_BARRIER之后的指令之前完成,并且所有CPU核看到都是这样的顺序。

2. 5G L2的代码存在的问题

5G L2的代码中,GET(data)接口的MEMORY_BARRIER()位置是这样的:

GET(data)
{
    if (readCusor != writeCusor)
    {
        MEMORY_BARRIER();
        data = dataBuffer[readCusor];
        readCusor++;
    }
}

这里假定(1)语句中的readCusor变量受(2)语句中的加加操作影响,编译器和CPU会自动识别,不会对这两行代码做优化、调整,会严格按照字面顺序执行。

(1) data = dataBuffer[readCusor];
        (2) readCusor++;

这样的假定是存在问题的,因为编译器或CPU可能会“优化”成下面这样:

GET(data)
{
    if (readCusor != writeCusor)
    {
        MEMORY_BARRIER();
        readCusorTemp = readCusor;
        readCusor++;
        data = dataBuffer[readCusorTemp];
    }
}

这样在生产者PUT那边会先看到readCusor++,会在队列满的情况下,概率出现GET数据被覆盖的情况。 虽然概率低,但一出问题就是严重甚至致命问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容