使用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.
核心问题原因
共用单个结构体实例导致数据覆盖
主线程仅创建了一个Th_Range结构体实例,所有线程共用该指针。主线程循环创建线程时,会立即修改结构体的ThreadId、StartRow等字段,而新线程可能还未完成初始化并读取参数,就被主线程的下一次循环覆盖了内容。最终:- 线程0启动时,结构体已被修改为线程1的参数,导致线程0实际处理线程1的任务
- 最后一个线程创建后,主线程不再修改结构体,原本的线程0和线程4都读取到线程4的参数,出现两次输出
结构体内存分配错误
代码中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
相关产品推荐
相关产品推荐

