我认为描述我的问题的简单方法是在代码中演示它,所以这里是C中一个人为的例子,以突出我有兴趣回答的问题:对复杂的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()的调用,所以我想知道是否为非原子读取保留私有引用是一种有价值的优化,或者如果编译器本身足够聪明,可以自行完成类似的优化。
编辑:当将非原子指针取消引用原子值时,结果未定义。
'static state_t * const s_pCurrent =&s_stateList [0];'这是违反约束的。指针不仅是不兼容的,而且是一个非原子指针,指向一个原子类型。 – 2501
修正了在任何情况下,当语法正确时,当我将原子值引用分配给非原子指针(否则为正确类型)时,我从编译器中得不到任何抱怨。 –
编译器不指定语言,不应将其用作真值的来源。在6.5.16.1§1的第三段中:*和类型指向 在左边具有由右侧指向的所有类型的限定符; *因此,左指针必须具有右指针所具有的所有限定符。 – 2501