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

多线程求最大值为何未出现预期的竞态条件?

代码展示
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define SIZE 2 /* array size */
#define NUM_THREADS 2

int max_num = 0;

int list[SIZE] = {1, 2};

void print_list(void);
void* runner(void* param);
void parallel_find_max_num(void);

int main(int argc, char* argv[])
{
  parallel_find_max_num();

  return 0;
}

void* runner(void* param)
{
  int part = *(int *)param;
  int size = SIZE / NUM_THREADS;
  int start_idx = part * size;

  if(part == NUM_THREADS - 1)
    size += SIZE % NUM_THREADS;

  /* update max num */
  for(int i = 0; i < size; i++)
    if(max_num < list[start_idx + i])
      max_num = list[start_idx + i];
}

void parallel_find_max_num(void)
{
  pthread_t tid[NUM_THREADS];
  int param[2]= {0, 1};

  for(int i = 0; i < NUM_THREADS; i++)
    pthread_create(&tid[i], NULL, runner, &param[i]);
  for(int i = 0; i < NUM_THREADS; i++)
    pthread_join(tid[i], NULL);

  printf("Max Number = %d\n", max_num);
}
问题原因分析

你遇到的竞态条件触发概率极低,主要和以下几个因素有关:

  • 线程执行逻辑极短:每个线程仅需执行几次指令就能完成任务,操作系统线程调度的开销远大于这些指令的执行时间。大多数情况下,一个线程会完整执行完毕后,另一个线程才开始运行:要么线程0先把max_num设为1,线程1再判断1<2并更新为2;要么线程1先把max_num设为2,线程0判断2<1不成立,不会修改值。只有当操作系统恰好两个线程的关键指令间隙完成调度,才可能触发竞态。

  • CPU存储重排序与写缓冲:现代CPU会用写缓冲优化写操作,写指令会先写入核心本地的写缓冲,再异步刷新到主内存。如果线程1先执行写2的操作,但该值还在写缓冲未同步到主内存;此时线程0读取主内存的旧值0,执行写1的操作并同步到主内存;最后线程1的写缓冲才刷新,却被线程0的写操作覆盖?不对,真正导致结果为1的极端场景是:线程1的写2操作先同步到主内存,线程0的写1操作因写缓冲延迟,在之后才刷新到主内存,覆盖了主内存中的2。这种存储重排序的概率极低,需要硬件层面的极端时序配合。

  • 判断逻辑的特殊性:线程0处理的数值1小于线程1的2,只有当线程1的写入被线程0的写入覆盖时才会出错,反过来的情况(线程0的写入被线程1覆盖)不会产生错误结果,这进一步压缩了错误场景的出现概率。

解决方法

可以通过两种方式避免竞态:

  1. 给共享资源max_num的访问加互斥锁,保证判断+写入的原子性:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define SIZE 2 /* array size */
#define NUM_THREADS 2

int max_num = 0;
pthread_mutex_t mutex;

int list[SIZE] = {1, 2};

void* runner(void* param);
void parallel_find_max_num(void);

int main(int argc, char* argv[])
{
  pthread_mutex_init(&mutex, NULL);
  parallel_find_max_num();
  pthread_mutex_destroy(&mutex);
  return 0;
}

void* runner(void* param)
{
  int part = *(int *)param;
  int size = SIZE / NUM_THREADS;
  int start_idx = part * size;

  if(part == NUM_THREADS - 1)
    size += SIZE % NUM_THREADS;

  for(int i = 0; i < size; i++){
    pthread_mutex_lock(&mutex);
    if(max_num < list[start_idx + i])
      max_num = list[start_idx + i];
    pthread_mutex_unlock(&mutex);
  }
  return NULL;
}

void parallel_find_max_num(void)
{
  pthread_t tid[NUM_THREADS];
  int param[2]= {0, 1};

  for(int i = 0; i < NUM_THREADS; i++)
    pthread_create(&tid[i], NULL, runner, &param[i]);
  for(int i = 0; i < NUM_THREADS; i++)
    pthread_join(tid[i], NULL);

  printf("Max Number = %d\n", max_num);
}
  1. 先让每个线程计算本地区间的最大值,再用原子操作更新全局最大值,减少锁竞争:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdatomic.h>

#define SIZE 2 /* array size */
#define NUM_THREADS 2

atomic_int max_num = 0;

int list[SIZE] = {1, 2};

void* runner(void* param);
void parallel_find_max_num(void);

int main(int argc, char* argv[])
{
  parallel_find_max_num();
  return 0;
}

void* runner(void* param)
{
  int part = *(int *)param;
  int size = SIZE / NUM_THREADS;
  int start_idx = part * size;

  if(part == NUM_THREADS - 1)
    size += SIZE % NUM_THREADS;

  int local_max = 0;
  for(int i = 0; i < size; i++){
    if(local_max < list[start_idx + i])
      local_max = list[start_idx + i];
  }

  int current_max;
  do {
    current_max = atomic_load(&max_num);
  } while(local_max > current_max && !atomic_compare_exchange_weak(&max_num, &current_max, local_max));

  return NULL;
}

void parallel_find_max_num(void)
{
  pthread_t tid[NUM_THREADS];
  int param[2]= {0, 1};

  for(int i = 0; i < NUM_THREADS; i++)
    pthread_create(&tid[i], NULL, runner, &param[i]);
  for(int i = 0; i < NUM_THREADS; i++)
    pthread_join(tid[i], NULL);

  printf("Max Number = %d\n", atomic_load(&max_num));
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 20:37:01