对复杂C11原子类型和非原子读取优化的部分更新

时间:2017-02-21 22:04:23

标签: c struct atomic c11

我认为描述我的问题的简单方法是在代码中展示它,所以这里有一个人为的例子来强调我有兴趣回答的问题:

// Just some complex user defined type
typedef struct {
    ...
} state_t;

typedef struct {
    state_t states[16];
} state_list_t;

static _Atomic state_list_t s_stateList;
// For non-atomic reads
static state_t * const s_pCurrent = &s_stateList.states[0];

// Called from external threads
void get_state(state_list_t * pStateList)
{
    *pStateList = atomic_load(&s_stateList);
}

// Only called by 'this' thread
static void update_state(struct state_data_t const * pData)
{
    state_list_t stateList = atomic_load(&s_stateList);
    for (int i = 0; i < 16; i++)
    {
        // Do some updating on the data
        do_transition(&stateList[i], pData);
    }
    atomic_store(&s_stateList, stateList);
}

// Only called by 'this' thread
static void apply_state(state_t const * pState)
{
    atomic_store(&s_stateList[0], *pState);
}

// Only called by this thread
static bool check_state()
{
    // Check (read) some values in the current state
    return isOkay(s_pCurrent);
}

首先,我对任何语法错误道歉,但这应该得到重点......

前两个函数是C11原子的非常简单的用法,即一个线程正在读取另一个正在写入的值。我的具体问题实际上是关于最后两个函数,apply_state和check_state,它实际上只是归结为这些是否可以做。

在apply_state中,您可以看到它只是原子地更新结构的一部分,特别是数组的第一个元素。我的理解是,基本上_Atomic s_stateList的每个元素都被认为是原子的(非常像volatile),因此编译器对atomic_store调用很好,但是这可能发生在另一个线程从对象“原子地”读取时(即在get_state中) ),或同步基本上等同于每次调用中锁定/解锁相同的互斥锁?我可以看到它是如何可能的,因为它基本上是一个不同的变量(好吧,好的相同的地址,但如果我使用状态[1]?)它可能导致使用不同的互斥量。另外,如果state_t碰巧是无锁的,会发生什么?

我更有信心check_state函数在这里做的很好,因为它只对一个只由同一个线程修改的对象执行读操作,但我想知道我是不是这里遗漏了什么。我刚刚发现直接访问原子变量(我认为通过赋值或函数参数)的处理方式与对atomic_load()或atomic_store()的调用完全相同,所以我想知道是否保留非原子读取的私有引用是一个值得优化的,或者如果编译器足够聪明,可以自己完成类似的优化。

编辑:取消引用原子值的非原子指针时,结果是未定义的。

1 个答案:

答案 0 :(得分:2)

这不符合C11的原子模型,并且有充分的理由。 _Atomic在语法上只是一个限定符,语义上_Atomic是一个新类型。这反映在标准允许这种类型的大小和对齐与基础大小不同的事实上。

在广义原子类型的情况下,原子类型的允许实现是向用作锁的struct添加隐藏字段。通常,此类类型实现为“无锁定”,具有一些控制对数据访问的隐藏状态(在struct内或单独)。

标准只能通过将访问模型拼接在一起来保证您的无竞争力。如果你要求你的整个数据是原子可访问的(在整个数据的同时不可分割的操作意义上),模型只允许你这样做。

访问原子对象的各个字段具有未定义的行为。这意味着如果您的平台具有特定属性,则可以允许您访问各个字段。您必须阅读平台的文档并希望获得最佳,特别是它们不会从一个版本(编译器,处理器......)更改为另一个版本。