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

关于C内存序模型理解正确性及数据竞争的技术问询

C语言内存序模型练习:生产者-消费者代码分析

背景

正在学习C语言内存序模型,编写了一段生产者与消费者共享带“units”的“bucket”的代码,意图在生产者(P)与消费者(C)间建立如下happens before(hb)关系序列:

  • P: 写入bucket(填充)
  • P: release存储bucket_full
  • C: acquire加载bucket_full
  • C: 读写bucket(递减、打印)
  • C: release存储bucket_full
  • P: acquire加载bucket_full
  • P: 写入bucket(循环重复)

注:我知道有更简洁的实现生产者-消费者的方式,此代码仅为练习

问题

  1. 我是否正确建立了上述happens before关系?
  2. 代码是否实际存在数据竞争,只是未显现?

代码实现

#include <stdio.h>
#include <stdatomic.h>
#include <pthread.h>

int num_buckets = 30, bucket = 0, units_per_bucket = 12;
_Atomic int bucket_full = 0;

void *producer(void *arg){
    for(int i=0; i<num_buckets+1; ++i){
        //acquire load to wait for "empty bucket" signal
        while(atomic_load_explicit(&bucket_full, memory_order_acquire))
            sched_yield();
        if(i == num_buckets){ // no more buckets to fill
            atomic_store_explicit(&bucket_full, -1, memory_order_release);
            break;
        }
        bucket = units_per_bucket; // fill bucket
        printf("\n[%2i]: %d", i+1, bucket);
        // release store to signal "bucket is full"
        atomic_store_explicit(&bucket_full, 1, memory_order_release);
    }
    return arg;
}

void *consumer(void *arg){
    while(1){
        //acquire load to wait for "bucket is full" signal
        while(!atomic_load_explicit(&bucket_full, memory_order_acquire))
            sched_yield();
        if(bucket_full == -1) //no more buckets, exit
            break;
        bucket --;
        printf(" %d", bucket);
        if(bucket < 1) // release store to signal "bucket is empty"
            atomic_store_explicit(&bucket_full, 0, memory_order_release);
    }
    return arg;
}

int main(){
    pthread_t t0, t1;
    pthread_create(&t0, NULL, consumer, (void *)NULL);
    pthread_create(&t1, NULL, producer, (void *)NULL);
    pthread_join(t0, NULL);
    pthread_join(t1, NULL);
    printf("\n");
    return 0;
}

编译命令

gcc -std=c11 -pthread -O2 -Wall -Wextra -Werror -pedantic -pedantic-errors main.c -o main

问题解答

1. 是否正确建立目标happens before关系?

核心的happens before链条是正确建立的,完全符合预期:

  • 生产者填充bucket的操作在memory_order_release语义的bucket_full=1存储之前,因此前者happens before后者;
  • 消费者通过memory_order_acquire语义加载到bucket_full=1时,根据release-acquire同步规则,生产者的bucket_full=1存储操作happens before消费者的这次加载操作,进而消费者后续对bucket的递减、读取操作都happens after生产者的填充操作;
  • 消费者将bucket减至0后,执行memory_order_release语义的bucket_full=0存储,该操作happens before生产者通过memory_order_acquire加载到bucket_full=0的操作,进而生产者下一次填充bucket的操作happens after消费者的清空操作;
  • 循环过程中上述链条会重复,形成连贯的happens before序列。

但代码存在一个瑕疵:消费者中if(bucket_full == -1)是直接以非原子方式读取原子变量,不符合C标准对原子变量的访问要求(原子变量必须通过原子操作访问),可能导致编译器优化后无法及时读取到生产者设置的-1值,但这并不影响核心的happens before关系链条。

2. 是否存在数据竞争?

针对bucket的访问不存在数据竞争,原因如下:
数据竞争的定义是:两个不同线程对同一非原子内存位置的访问,至少一个是写入操作,且两者之间没有happens before关系。

  • 生产者仅在通过acquire加载确认bucket_full=0后才会写入bucket,此时消费者已经完成了对bucket的清空操作(消费者只会在bucket<1时才会release存储bucket_full=0),因此生产者的写入与消费者之前的读写存在明确的happens before关系;
  • 消费者仅在通过acquire加载确认bucket_full=1后才会读写bucket,此时生产者已经完成填充,消费者的读写与生产者的写入存在明确的happens before关系;
  • 消费者多次递减bucket的过程中,生产者会持续等待bucket_full=0,不会访问bucket,不存在并发访问。

但需注意:消费者直接非原子读取原子变量bucket_full的行为属于未定义行为,虽然这不是数据竞争,但可能导致程序行为异常(比如无法正确退出循环),建议改为if(atomic_load_explicit(&bucket_full, memory_order_acquire) == -1)。


内容的提问来源于stack exchange,提问作者onlycparra

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 03:46:07