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

使用pthread_join时输出异常的原因排查与解决求助

多线程pthread_join位置异常问题排查与解决

问题现象

当将pthread_join(thread[i],NULL)放在线程创建循环外部时,输出出现异常:

  • ThreadID=0的线程未输出Median函数执行结果
  • 最后一个线程(ThreadID=4)的Median函数结果输出了两次

异常输出如下:

ThreadID= 0, startRow= 0, endRow= 0      // first thread doesn't call the median func
ThreadID= 1, startRow= 1, endRow= 1
ThreadID 1 numOfBright 0 numOfDark 1 numOfNormal 4
ThreadID= 2, startRow= 2, endRow= 2
ThreadID 2 numOfBright 0 numOfDark 1 numOfNormal 4
ThreadID= 3, startRow= 3, endRow= 3
ThreadID 3 numOfBright 0 numOfDark 0 numOfNormal 5
ThreadID= 4, startRow= 4, endRow= 4
ThreadID 4 numOfBright 0 numOfDark 5 numOfNormal 0  
ThreadID 4 numOfBright 0 numOfDark 5 numOfNormal 0 // last thread is calling the median func two times.

核心问题原因

  1. 共用单个结构体实例导致数据覆盖
    主线程仅创建了一个Th_Range结构体实例,所有线程共用该指针。主线程循环创建线程时,会立即修改结构体的ThreadId、StartRow等字段,而新线程可能还未完成初始化并读取参数,就被主线程的下一次循环覆盖了内容。最终:

    • 线程0启动时,结构体已被修改为线程1的参数,导致线程0实际处理线程1的任务
    • 最后一个线程创建后,主线程不再修改结构体,原本的线程0和线程4都读取到线程4的参数,出现两次输出
  2. 结构体内存分配错误
    代码中malloc(sizeof(struct Th_Range*))分配的是指针类型的大小(通常4/8字节),而非Th_Range结构体的实际大小,会引发内存越界,进一步加剧数据混乱。

正确解决方法

1. 为每个线程分配独立的参数结构体

在循环中为每个线程创建独立的Th_Range实例,确保每个线程的参数不会被主线程修改。

2. 修正内存分配错误

使用malloc(sizeof(struct Th_Range))分配结构体实际大小,或用C++的new关键字更符合代码风格。

3. 线程结束后释放内存

每个线程处理完任务后,释放自身的参数结构体,避免内存泄漏。

修改后的关键代码片段

主线程部分

pthread_t* threads = new pthread_t[num_threads];
if (n == num_threads) {  
    int thread_status;
    for (int i = 0; i < num_threads; i++) {
        // 为每个线程创建独立的参数结构体
        struct Th_Range* RANGE = new Th_Range();
        RANGE->SizeOfImage = r;
        RANGE->ThreadId = i;
        RANGE->StartRow = RANGE->EndRow = i;
        
        cout << "ThreadID= " << i << ", startRow= " << RANGE->StartRow << ", endRow= " << RANGE->EndRow << endl;
        
        thread_status = pthread_create(&threads[i], NULL, Median, RANGE);
        if (thread_status) 
            exit(-1);
    }
    // 在循环外部统一等待所有线程结束
    for (int i = 0; i < num_threads; i++) {
        pthread_join(threads[i], NULL);
    }
    // 释放线程数组内存
    delete[] threads;
}

Median函数部分

void* Median(void* par)
{
    struct Th_Range* Num = (struct Th_Range*)par;
    int StartRow = Num->StartRow;
    int EndRow = Num->EndRow;
    int Size = Num->SizeOfImage;    
    int Neighbour[9] = { 0 };
    int dark = 0, bright = 0, normal = 0;

    if (EndRow == StartRow)   
        EndRow += 2;
    else
        EndRow++;

    for (int i = StartRow +1; i < EndRow ; i++)
    {
        for (int j = 1; j < Size - 1; j++)
        {
            Neighbour[0] = Oimage[i - 1][j - 1];
            Neighbour[1] = Oimage[i - 1][j];
            Neighbour[2] = Oimage[i - 1][j + 1];
            Neighbour[3] = Oimage[i][j - 1];
            Neighbour[4] = Oimage[i][j];
            Neighbour[5] = Oimage[i][j + 1];
            Neighbour[6] = Oimage[i + 1][j - 1];
            Neighbour[7] = Oimage[i + 1][j];
            Neighbour[8] = Oimage[i + 1][j + 1];

            sort(Neighbour, Neighbour + 9);
            Fimage[i][j] = Neighbour[4];
            
            // 仅修改全局统计变量时加锁,减少不必要的性能损耗
            pthread_mutex_lock(&Pixels);
            if (Neighbour[4] > 200) {
                bright++;
                numOfBright++;
            }
            else if (Neighbour[4] < 50) {
                dark++;
                numOfDark++;
            }
            else {
                normal++;
                numOfNormal++;
            }
            pthread_mutex_unlock(&Pixels);
        }
    }

    pthread_mutex_lock(&Pixels2);
    cout << "ThreadID " << Num->ThreadId << " numOfBright " << bright << " numOfDark " << dark << " numOfNormal " << normal << endl;
    pthread_mutex_unlock(&Pixels2);

    // 释放当前线程的参数结构体
    delete Num;
    pthread_exit(NULL);
}

额外优化建议

  • 缩小锁的范围:原代码中sort和Fimage修改都加了锁,但Fimage的每个元素由不同线程独立处理(各线程处理不同行),无需加锁;sort是局部操作,也不需要锁。仅在修改全局统计变量时加锁即可,能显著提升多线程性能。
  • 初始化互斥锁:需在main函数开头添加pthread_mutex_init(&Pixels, NULL);和pthread_mutex_init(&Pixels2, NULL);,程序结束前添加pthread_mutex_destroy(&Pixels);和pthread_mutex_destroy(&Pixels2);,避免未初始化锁导致的未定义行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 09:05:33