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

pthread多线程程序异常输出分析(5种场景)及修复方案咨询

pthread多线程程序输出不稳定的原因及修复方法

输出产生多种结果的原因

原程序中所有子线程都接收了主线程变量i的内存地址,而非创建线程时i的副本。由于操作系统的线程调度具有不确定性,子线程执行时读取到的i值是主线程当前的实时值,而非创建线程时的初始值,从而导致多种不同输出。具体表现对应的核心原因包括:

  • 线程调度时机不确定:操作系统可随时切换线程,主线程创建线程后可能继续执行i++,子线程可能在i多次自增后才被调度,读取到更大的i值。例如主线程快速完成4次线程创建,所有子线程都在循环结束后执行,此时i已变为4,所有线程都会输出Thread number 4。
  • 共享变量无同步机制:i是主线程与子线程的共享变量,没有同步锁或其他机制保证子线程读取i时主线程不会修改它。比如线程0还未读取i,主线程已将i自增到1,线程0就会读取到1。
  • 线程创建与执行的延迟:pthread_create仅完成线程创建,子线程不会立即执行。主线程可能在子线程启动前完成多次循环,导致子线程读取到后续的i值。例如线程3创建后,主线程的i已变为4,线程3执行时就会输出4。
  • 极端场景下的指令重排序:编译器或CPU可能对指令进行重排,虽然printf是IO操作限制了重排,但极端情况下仍可能导致子线程读取到意外的i值。
  • 多线程并发访问共享变量:i++并非原子操作(分为读取、加1、写回三步),若主线程在执行i++的过程中切换到子线程,子线程可能读取到i的中间状态(虽然32位系统上int的读写通常是原子的,但调度问题仍会导致值不确定)。

以上原因共同导致程序可能出现多种输出,比如0、1、2、3(理想顺序)、1、1、3、4、0、2、2、3、4、4、4、4、0、0、0、0等。

修复方法

针对核心问题,我们需要让每个子线程获取创建时i的独立副本,而非共享的内存地址,以下是几种可行方案:

方案1:为每个线程分配独立内存存储i的副本

通过malloc为每个线程分配独立的内存空间,存储当前循环的i值,线程执行完毕后释放内存。这种方法兼容性最好,不依赖系统架构:

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#define NUM_THREADS 4

void *threadFunc(void *pArg)
{ 
    int myNum = *(int*)pArg;
    free(pArg); // 释放分配的内存
    printf("Thread number %d\n", myNum);
    return NULL;
}

int main(void) 
{
    int i;
    pthread_t tid[NUM_THREADS];
    for(i = 0; i < NUM_THREADS; i++) 
    {
        int *p = malloc(sizeof(int));
        *p = i;
        pthread_create(&tid[i], NULL, threadFunc, p);
    }
    for(i = 0; i < NUM_THREADS; i++)
    { 
        pthread_join(tid[i], NULL);
    }
    return 0;
}

方案2:直接传递i的值作为参数(简洁但依赖系统架构)

利用void*可以容纳int大小的特性(现代系统普遍满足),将i强制转换为void*传递给线程,线程内部再转换回int:

#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 4

void *threadFunc(void *pArg)
{ 
    int myNum = (int)pArg;
    printf("Thread number %d\n", myNum);
    return NULL;
}

int main(void) 
{
    int i;
    pthread_t tid[NUM_THREADS];
    for(i = 0; i < NUM_THREADS; i++) 
    {
        pthread_create(&tid[i], NULL, threadFunc, (void*)i);
    }
    for(i = 0; i < NUM_THREADS; i++)
    { 
        pthread_join(tid[i], NULL);
    }
    return 0;
}

注意:若系统中int的大小超过void*(极端场景),这种方法会出错,因此方案1更通用。

方案3:使用同步机制强制线程串行读取(不推荐,损失并行性)

通过互斥锁和条件变量,让主线程等待子线程读取完i后再继续自增,这种方法会让线程串行执行,失去多线程并行优势,仅适用于特殊场景:

#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 4

pthread_mutex_t mutex;
pthread_cond_t cond;
int ready = 0;

void *threadFunc(void *pArg)
{ 
    int *p = (int*)pArg;
    pthread_mutex_lock(&mutex);
    ready = 1;
    pthread_cond_signal(&cond);
    int myNum = *p;
    pthread_mutex_unlock(&mutex);
    printf("Thread number %d\n", myNum);
    return NULL;
}

int main(void) 
{
    int i;
    pthread_t tid[NUM_THREADS];
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    
    for(i = 0; i < NUM_THREADS; i++) 
    {
        pthread_mutex_lock(&mutex);
        ready = 0;
        pthread_create(&tid[i], NULL, threadFunc, &i);
        while(!ready) {
            pthread_cond_wait(&cond, &mutex);
        }
        pthread_mutex_unlock(&mutex);
    }
    for(i = 0; i < NUM_THREADS; i++)
    { 
        pthread_join(tid[i], NULL);
    }
    
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 11:55:20