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

全局计数器与并发线程:为何线程1结束时计数器值异常偏高?

线程计数异常问题分析与修正

问题原因分析

你的代码逻辑导致thread1打印的计数器值偏高,核心原因如下:

  1. 线程调度与结束顺序
    你使用pthread_mutex_trylock尝试获取锁,锁被占用时线程会直接空转等待。实际系统调度中,先创建的thread1往往能获得更多初始执行机会,会先完成自身200万次计数循环(i达到MAX)。此时thread2仍在运行并继续递增计数器,因此thread1打印的是它结束瞬间的计数器值(约380-390万,即自身200万加上thread2已完成的180-190万);而thread2作为最后结束的线程,会打印最终的400万。

  2. 未初始化共享变量
    通过malloc分配的counter结构体未初始化,counter->value的初始值是内存随机垃圾值,可能导致计数结果出现不可预期的偏差。

实现的不合理之处

  • 低效的锁使用:pthread_mutex_trylock会导致线程空转浪费CPU,对于互斥访问场景,pthread_mutex_lock(阻塞式等待锁)才是更合理的选择。
  • 类型匹配问题:MAX是unsigned long类型,但i是uint32_t、counter->value是int,类型不统一存在潜在隐患;虽然400万未超出32位int范围,但建议使用uint32_t避免溢出风险。

修正后的代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdint.h>

#define MAX 2000000UL
pthread_mutex_t lock;

struct shared_data
{
    uint32_t value;     /* 使用无符号32位类型,避免溢出隐患 */
};

struct shared_data *counter;

void* threadFunc(void * tid){
    uint32_t i = 0;
    uint32_t thread_id = *(uint32_t*)tid;
    while(i < MAX){
        pthread_mutex_lock(&lock); // 改用阻塞式锁,避免空转浪费CPU
        counter->value++;
        pthread_mutex_unlock(&lock);
        i++;
    }
    printf("I am thread %u. I counted %u times. Global counter = %u\n", thread_id, i, counter->value);
    return NULL;
}

int main() {
    counter = (struct shared_data *) malloc(sizeof(struct shared_data));
    counter->value = 0; // 明确初始化计数器为0
    printf("Initial Counter Value: %u\n", counter->value);
    
    pthread_t thread1;
    pthread_t thread2;
    uint32_t tid1 = 1, tid2 = 2;
    pthread_mutex_init(&lock, NULL);
    
    pthread_create(&thread1, NULL, threadFunc, &tid1);
    pthread_create(&thread2, NULL, threadFunc, &tid2);
    
    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    
    printf("Final Counter: %u\n", counter->value);
    // 释放资源,避免泄漏
    pthread_mutex_destroy(&lock);
    free(counter);
    return 0;
}

修正说明

  1. 初始化共享变量:将counter->value初始化为0,消除随机初始值的影响。
  2. 优化锁逻辑:用pthread_mutex_lock替代trylock,阻塞式等待锁避免CPU空转,同时保证共享变量的互斥访问更可靠。
  3. 统一变量类型:将counter->value改为uint32_t,与MAX、i类型保持一致,避免类型转换隐患。
  4. 复用线程函数:合并两个线程函数,通过传递线程ID区分,减少代码冗余。
  5. 资源释放:添加锁销毁和内存释放操作,避免资源泄漏。

修正后,线程结束顺序仍可能随机,但先结束的线程打印的计数器值会在200万-400万之间波动,最终计数器值稳定为400万,符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 06:24:35