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

pthread_create传参异常:添加sleep后恢复正常,是否必须使用sleep?

pthread_create传递参数异常:sleep真的必要吗?

问题描述

我在重复测试pthread_create与pthread_join时发现,传递给线程函数的参数值无法正确传递。但如果在pthread_create后添加sleep()代码,参数就能正常传递。我不清楚其中原因,想请教sleep函数是否真的有必要使用?

测试代码

版本1(无usleep)

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

void *thread_func(void *arg)
{
    printf("I am thread #%d\n", *(int *)arg);
}

int main(int argc, char *argv[])
{
    pthread_t t1[7];

    while(1){
        for(int i = 0; i < 7; i++){
            pthread_create(&t1[i], NULL, &thread_func, &i);
        }

        sleep(10);
        
        for(int j = 0; j < 7; j++){
            pthread_join(t1[j], NULL);
        }
        
        sleep(10);
    }   

    return 0;
}

版本2(添加usleep(1))

int main(int argc, char *argv[])
{
    pthread_t t1[7];

    while(1){
        for(int i = 0; i < 7; i++){
            pthread_create(&t1[i], NULL, &thread_func, &i);
            usleep(1);
        }

        sleep(10);
        
        for(int j = 0; j < 7; j++){
            pthread_join(t1[j], NULL);
        }
        
        sleep(10);
    }   

    return 0;
}

原因分析

核心问题不是线程创建的时机,而是你传递给线程的是循环变量i的地址,而非i的当前值。

主线程的for循环执行速度极快,远快于子线程启动并执行printf的速度。当子线程终于开始读取*(int*)arg时,主线程的i早就已经变了——可能已经循环到了下一个值,甚至整个循环结束后i已经等于7了。

添加sleep或usleep只是让主线程暂停,给子线程留出足够时间读取i的当前值,但这是治标不治本的临时方案,完全没必要依赖sleep来解决问题。

正确解决方案

你需要确保每个线程拿到的是独立、不会被主线程修改的参数值,有几种常用方法:

方法1:为每个线程分配独立内存

用malloc给每个线程单独分配int空间,存储当前i的值,线程执行完毕后释放内存:

void *thread_func(void *arg)
{
    int num = *(int *)arg;
    free(arg); // 释放分配的内存
    printf("I am thread #%d\n", num);
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t t1[7];

    while(1){
        for(int i = 0; i < 7; i++){
            int *arg = malloc(sizeof(int));
            *arg = i;
            pthread_create(&t1[i], NULL, &thread_func, arg);
        }

        sleep(10);
        
        for(int j = 0; j < 7; j++){
            pthread_join(t1[j], NULL);
        }
        
        sleep(10);
    }   

    return 0;
}

方法2:直接传递值(适用于小范围整数)

如果i的取值范围在指针能容纳的范围内(多数系统中int和指针长度相同),可以直接把i强制转换成void*传递,线程里再转回来:

void *thread_func(void *arg)
{
    int num = (int)arg;
    printf("I am thread #%d\n", num);
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t t1[7];

    while(1){
        for(int i = 0; i < 7; i++){
            pthread_create(&t1[i], NULL, &thread_func, (void*)i);
        }

        sleep(10);
        
        for(int j = 0; j < 7; j++){
            pthread_join(t1[j], NULL);
        }
        
        sleep(10);
    }   

    return 0;
}

方法3:用数组存储独立参数

提前定义一个数组,每个元素存储对应线程的参数值,传递数组元素的地址:

int main(int argc, char *argv[])
{
    pthread_t t1[7];
    int args[7]; // 存储每个线程的参数

    while(1){
        for(int i = 0; i < 7; i++){
            args[i] = i;
            pthread_create(&t1[i], NULL, &thread_func, &args[i]);
        }

        sleep(10);
        
        for(int j = 0; j < 7; j++){
            pthread_join(t1[j], NULL);
        }
        
        sleep(10);
    }   

    return 0;
}

总结

sleep完全没必要用,它只是掩盖了参数共享导致的竞态问题。解决的核心是让每个线程拿到不会被主线程修改的独立参数,上面几种方法都能从根本上解决问题。

内容的提问来源于stack exchange,提问作者김태정

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 07:27:39