Atomic Pointer解引用异常求助:内存栅栏与原子指针使用疑惑
内存栅栏与Atomic指针同步问题分析
我编写了一个测试程序以理解内存栅栏(memory fences)与Atomic指针的用法。程序包含两个线程:一个线程读取g_ptr->int_ptr->id,另一个线程malloc新内存并更新g_ptr->int_ptr的引用,且始终保持g_ptr->int_ptr->id的值为2。我知道可通过锁实现同步,但暂时未使用。令我困惑的是,该程序在ARM/x86_64架构CPU上均会触发错误条件(检测到g_ptr->int_ptr->id不等于2,但打印时id仍为2),请帮忙分析原因。
测试代码
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <stdatomic.h> typedef struct _int_st { int id; } int_st; typedef struct _dummy_st { int_st * _Atomic int_ptr; } dummy_st; dummy_st *g_ptr; void* func(void* arg) { pthread_detach(pthread_self()); while(1) { int_st * _Atomic tmp_ptr = g_ptr->int_ptr; int_st * _Atomic new_ptr = malloc(sizeof(int_st)); new_ptr->id = 2; g_ptr->int_ptr = new_ptr; atomic_thread_fence(memory_order_release); free(tmp_ptr); } pthread_exit(NULL); } void fun() { pthread_t ptid; g_ptr = malloc(sizeof(dummy_st)); g_ptr->int_ptr = malloc(sizeof(int_st)); g_ptr->int_ptr->id = 2; printf("Value of ptr is: %p and size is: %lu \n", g_ptr->int_ptr, sizeof(g_ptr->int_ptr)); atomic_thread_fence(memory_order_release); pthread_create(&ptid, NULL, &func, NULL); int it_num = 0; while(1) { printf("Trying iteration num: %d \n", it_num); if (g_ptr->int_ptr->id != 2) { printf("Error found \n"); pthread_cancel(ptid); printf("Value of ptr is: %p and id is: %d \n", g_ptr->int_ptr, g_ptr->int_ptr->id); break; } it_num++; } printf("This line will be printed after thread ends\n"); return; } int main() { fun(); return 0; }
运行输出示例
Value of ptr is: 0x600003c64040 and size is: 8 Trying iteration num: 0 Trying iteration num: 1 Trying iteration num: 2 Trying iteration num: 3 Trying iteration num: 4 Trying iteration num: 5 Trying iteration num: 6 Trying iteration num: 7 Trying iteration num: 8 Trying iteration num: 9 Trying iteration num: 10 Trying iteration num: 11 Trying iteration num: 12 Trying iteration num: 13 Trying iteration num: 14 Trying iteration num: 15 Trying iteration num: 16 Trying iteration num: 17 Trying iteration num: 18 Trying iteration num: 19 Trying iteration num: 20 Trying iteration num: 21 Error found Value of ptr is: 0x600003c70000 and id is: 2 This line will be printed after thread ends
问题原因分析
1. 释放后内存访问导致未定义行为
读取线程的g_ptr->int_ptr->id操作拆分为两步独立的原子操作:先读取指针地址,再读取该地址的id值。这中间存在一个时序窗口:
- 读取线程先获取到旧指针地址
- 更新线程随即free该旧指针,部分malloc实现会在释放的内存中填充标记值(如
0xdeadbeef这类非2的数值) - 读取线程此时读取已释放内存的
id,触发错误条件 - 之后更新线程再次malloc,刚好重新分配了这块内存并设置
id=2,打印时就读到了正确的值
2. 内存栅栏位置错误
更新线程中的atomic_thread_fence(memory_order_release)放在了g_ptr->int_ptr = new_ptr;之后,完全起不到同步作用。release栅栏的作用是保证之前的写操作(new_ptr->id=2)在原子指针更新前完成,确保其他线程看到原子指针更新时,对应的id已经是2。正确的位置应该是原子指针赋值之前。
3. 读取线程缺乏同步语义
读取线程没有使用acquire语义的原子加载,无法保证读取指针和读取id操作的顺序一致性。即使指针是原子类型,两步操作之间没有同步,依然可能读取到旧指针对应内存的 stale 值。
修复建议
- 调整内存栅栏位置:将更新线程的release栅栏移到原子指针赋值前:
new_ptr->id = 2; atomic_thread_fence(memory_order_release); g_ptr->int_ptr = new_ptr; free(tmp_ptr); - 读取线程使用acquire语义:用
atomic_load_explicit获取原子指针,确保后续读取id时拿到最新值:int_st* curr_ptr = atomic_load_explicit(&g_ptr->int_ptr, memory_order_acquire); if (curr_ptr->id != 2) { // 后续错误处理逻辑 } - 避免释放后内存访问:采用延迟释放(如引用计数),确保旧指针在没有线程访问时再释放,彻底消除未定义行为。
内容的提问来源于stack exchange,提问作者Sourav Sen
相关产品推荐
相关产品推荐

