You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 12:42:10