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

如何证明多线程代码中sum += local_sum语句需要互斥锁?

多线程累加全局变量的同步问题

问题描述

我编写了一个简单的多线程C程序,功能是读取大量数字并最终累加到全局变量sum中。每个线程会先使用local_sum变量计算局部和,再将其加到全局sum中。目前有两个疑问:

  1. thread_runner函数中的sum += local_sum;语句是否需要加锁?我确信该语句需要加锁,但当前程序总能得到正确结果。
  2. 若确实需要锁,如何证明该未加锁的实现是错误的?

注:仅关注互斥锁作为同步原语,忽略返回值检查。原代码如下:

#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void *thread_runner(void *index);
// pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

long array[100000000];
long sum = 0;

int main() {
    /* Insert values in the array */
    long i = 0;
    for (i = 0; i < 100000000; i++) {
        array[i] = i+1;
    }

    /* Declare thread variables */
    pthread_t t1, t2, t3, t4;
    int index1 = 0;
    int index2 = 25000000;
    int index3 = 50000000;
    int index4 = 75000000;

    /* Create 4 threads */
    int rc = pthread_create(&t1, NULL, thread_runner, &index1);
    rc = pthread_create(&t2, NULL, thread_runner, &index2);
    rc = pthread_create(&t3, NULL, thread_runner, &index3);
    rc = pthread_create(&t4, NULL, thread_runner, &index4);

    /* Wait for threads to complete */
    rc = pthread_join(t1, NULL);
    rc = pthread_join(t2, NULL);
    rc = pthread_join(t3, NULL);
    rc = pthread_join(t4, NULL);

    printf("Sum: %ld\n", sum);

    return 0;
}


void *thread_runner(void *index) {
    int i = 0;
    int i_index = *((int*)index);
    long local_sum = 0;
    for (i = i_index; i < i_index+25000000; i++) {
        local_sum += array[i];
    }
    // Do i need to lock the following statement ?
    sum += local_sum;
}

解答

结论:必须加锁

sum += local_sum;看起来是一行代码,实际是三步非原子操作:

  1. 读取当前全局sum的内存值到寄存器
  2. 寄存器中的值加上local_sum
  3. 将计算结果写回全局sum的内存地址

多线程并发执行时,若两个线程同时执行第一步(读取到同一个sum值),各自完成加法后写回,就会导致其中一个线程的累加结果被覆盖,最终sum的实际值会小于预期。

为什么当前程序总能得到正确结果?

你的测试场景不具备并发冲突条件:

  • 每个线程计算局部和的耗时极长,远超过sum += local_sum的执行时间,导致四个线程几乎是串行执行最后的累加操作,自然不会触发竞态条件。
  • 线程创建、执行的顺序刚好让它们的累加操作没有重叠,相当于没用到多线程的并发特性。

如何证明未加锁实现是错误的?

可以通过以下两种方式主动触发竞态条件:

  1. 缩短线程计算时间,高频累加
    把局部计算逻辑简化,让线程循环执行多次累加操作,这样多个线程会高频并发修改sum,很快就能看到结果错误。修改后的thread_runner示例:

    void *thread_runner(void *index) {
        long local_sum = 1;
        // 循环10万次累加,放大竞态条件
        for (int i = 0; i < 100000; i++) {
            sum += local_sum;
        }
        return NULL;
    }
    

    预期结果应为4*100000=400000,但实际运行会得到远小于该值的结果,因为大量累加操作被覆盖。

  2. 在累加前插入随机延迟
    在sum += local_sum;之前加入短暂随机延迟,让线程的累加操作大概率重叠,触发竞态条件。修改后的代码示例:

    void *thread_runner(void *index) {
        int i = 0;
        int i_index = *((int*)index);
        long local_sum = 0;
        for (i = i_index; i < i_index+25000000; i++) {
            local_sum += array[i];
        }
        // 随机延迟,让线程累加操作错开
        usleep(rand() % 1000);
        sum += local_sum;
        return NULL;
    }
    

    同时需要在main函数开头初始化随机数种子:srand(time(NULL));,多次运行后会偶尔出现结果错误。

正确的加锁实现

取消原代码中互斥锁的注释,在累加前后加锁、解锁,保证sum += local_sum的原子性:

#include <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void *thread_runner(void *index);
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 取消注释

long array[100000000];
long sum = 0;

// main函数内容不变...

void *thread_runner(void *index) {
    int i = 0;
    int i_index = *((int*)index);
    long local_sum = 0;
    for (i = i_index; i < i_index+25000000; i++) {
        local_sum += array[i];
    }
    pthread_mutex_lock(&lock); // 加锁
    sum += local_sum;
    pthread_mutex_unlock(&lock); // 解锁
    return NULL;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 08:40:32