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

单核心与双核心下badcnt.c的cnt结果范围是否存在差异?

Understanding Race Conditions in badcnt.c: Dual-Core vs Uniprocessor

Great question—this cuts to a fundamental concept in concurrent programming that Bryant emphasizes in Computer Systems: A Programmer's Perspective. Let's break down the behavior step by step, starting with your provided code:

The problematic code (badcnt.c):

#include "csapp.h"
void *thread(void *vargp);

volatile long cnt = 0;

int main(int argc, char **argv){
    long niters;
    pthread_t tid1, tid2;
    niters = atoi(argv[1]);

    Pthread_create(&tid1, NULL, thread, &niters);
    Pthread_create(&tid2, NULL, thread, &niters);
    Pthread_join(tid1, NULL);
    Pthread_join(tid2, NULL);

    printf("cnt = %ld\n", cnt);
}

void *thread(void *vargp){
    long i;
    long niters = *((long *) vargp);
    for(i = 0; i < niters; i++)
        cnt++;
    return NULL;
}

Recap: Uniprocessor Behavior (10 ≤ cnt ≤ 20)

You’re correct about the uniprocessor case. Here’s why the range is constrained to 10–20 (assuming niters=10):

  • The cnt++ operation isn’t atomic—it translates to three distinct machine instructions: load cnt into a register, add 1 to the register, store the new value back to memory.
  • On a single core, only one thread runs at a time. Thread switches can only occur between complete instructions, not mid-operation.
  • Worst-case scenario: Thread 1 loads cnt=0, then gets preempted. Thread 2 runs all 10 iterations, incrementing cnt to 10. Thread 1 resumes, adds 1 to its cached value (0 → 1) and stores it, setting cnt=1. Thread 1 then finishes its remaining 9 iterations, each time incrementing the current cnt value, resulting in a final count of 10.
  • Best-case scenario: No preemption happens—each thread runs all 10 iterations consecutively, so cnt ends at 20.

Dual-Core Behavior: Wider Range (1 ≤ cnt ≤ 20)

On a dual-core system, the result range does differ significantly—the lower bound can drop all the way to 1, while the upper bound stays at 20. The key difference is true parallel execution:

  • On two cores, threads run simultaneously, not just interleaved. Both can execute their cnt++ instructions at the exact same moment.
  • Even with volatile (which prevents compiler optimizations like caching cnt in a register), there’s no hardware synchronization. Two threads can both load the same cnt value (e.g., 0) from memory (or their local CPU caches) at the same time.
  • Each thread adds 1 to their loaded value, then stores the result back. If both store the same value (e.g., 1), the second store overwrites the first—meaning two cnt++ operations only increment cnt by 1.
  • Extreme worst case: Both threads load the initial cnt=0 for all 10 iterations before either writes back. Then both threads add 1 to 0 (getting 1) and store 1 back 10 times each. All stores overwrite each other, leaving cnt=1 as the final value.

Critical Takeaway

The root issue is that cnt++ is a non-atomic read-modify-write operation. Uniprocessors limit race conditions to interleaved execution, but multiprocessors allow true parallelism, leading to more frequent and severe race conditions that expand the possible range of cnt values. To fix this, you’d need to use synchronization primitives like mutexes (pthread_mutex_t) or atomic operations to ensure cnt++ happens atomically.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:00:52