使用pthread_detach时的内存泄漏问题(哲学家就餐问题场景)
哲学家就餐问题模拟程序的内存泄漏排查
问题背景
我正在开发哲学家就餐问题的模拟程序,需模拟多位哲学家围坐餐桌、共享叉子就餐的行为,严格遵循mutex、threads等资源管理约束,避免死锁、饥饿、竞态条件等经典问题。添加就餐次数限制后,运行check_death函数的线程引发了内存泄漏。
相关代码
void *philo_life(void *phi) { t_philo *philo; pthread_t t; philo = (t_philo *)phi; if (philo->n % 2 == 0) ft_usleep(philo->info->t_eat / 10); while (!is_dead(philo, 0)) { pthread_create(&t, NULL, check_death, phi); take_fork(philo); philo_eat(philo); pthread_detach(t); if (philo->m_count == philo->info->n_eat) { pthread_mutex_lock(&philo->info->m_stop); if (++philo->info->philo_eat == philo->info->n_philo) { pthread_mutex_unlock(&philo->info->m_stop); is_dead(philo, 2); } pthread_mutex_unlock(&philo->info->m_stop); return (NULL); } } return (NULL); } int is_dead(t_philo *philo, int nb) { pthread_mutex_lock(&philo->info->dead); if (nb) philo->info->stop = 1; if (philo->info->stop) { pthread_mutex_unlock(&philo->info->dead); return (1); } pthread_mutex_unlock(&philo->info->dead); return (0); } void *check_death(void *phi) { t_philo *philo; philo = (t_philo *)phi; ft_usleep(philo->info->t_die + 1); pthread_mutex_lock(&philo->info->m_eat); pthread_mutex_lock(&philo->info->m_stop); if (!is_dead(philo, 0) && timestamp() - \ philo->last_eat >= (long)(philo->info->t_die)) { pthread_mutex_unlock(&philo->info->m_eat); pthread_mutex_unlock(&philo->info->m_stop); print(philo, " died\n"); is_dead(philo, 1); } pthread_mutex_unlock(&philo->info->m_eat); pthread_mutex_unlock(&philo->info->m_stop); return (NULL); }
Valgrind检测输出
==439355== ==439355== HEAP SUMMARY: ==439355== in use at exit: 1,088 bytes in 4 blocks ==439355== total heap usage: 16 allocs, 12 frees, 5,272 bytes allocated ==439355== ==439355== 1,088 bytes in 4 blocks are possibly lost in loss record 1 of 1 ==439355== at 0x484DA83: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==439355== by 0x40147D9: calloc (rtld-malloc.h:44) ==439355== by 0x40147D9: allocate_dtv (dl-tls.c:375) ==439355== by 0x40147D9: _dl_allocate_tls (dl-tls.c:634) ==439355== by 0x49087B4: allocate_stack (allocatestack.c:430) ==439355== by 0x49087B4: pthread_create@@GLIBC_2.34 (pthread_create.c:647) ==439355== by 0x401803: philo_life (philo.c:74) ==439355== by 0x4907AC2: start_thread (pthread_create.c:442) ==439355== by 0x4998A03: clone (clone.S:100) ==439355== ==439355== LEAK SUMMARY: ==439355== definitely lost: 0 bytes in 0 blocks ==439355== indirectly lost: 0 bytes in 0 blocks ==439355== possibly lost: 1,088 bytes in 4 blocks ==439355== still reachable: 0 bytes in 0 blocks ==439355== suppressed: 0 bytes in 0 blocks ==439355== ==439355== For lists of detected and suppressed errors, rerun with: -s ==439355== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
问题分析
从Valgrind输出可以定位到泄漏点是philo_life中循环调用的pthread_create,原因如下:
- 线程创建过于频繁:每次就餐循环都新建一个
check_death线程,即使调用pthread_detach,当哲学家因达到就餐次数提前退出时,部分未执行完的线程资源无法被及时回收。 - 线程生命周期不匹配:
check_death线程单次睡眠t_die+1时间后才执行检测,若哲学家在这段时间内完成就餐退出,线程的内存资源会成为"可能丢失"的状态。 - 设计冗余:每个哲学家无需每次就餐都新建检测线程,重复创建销毁线程不仅浪费资源,还增加了内存泄漏风险。
解决方案
优化思路
- 每个哲学家对应一个常驻的死亡检测线程,而非循环创建。
- 使用
pthread_join替代pthread_detach,确保主线程退出前等待检测线程完成,回收资源。 - 调整
check_death为循环检测逻辑,避免长时间睡眠导致的资源滞留。
修改后的代码示例
void *philo_life(void *phi) { t_philo *philo; pthread_t death_thread; philo = (t_philo *)phi; // 仅创建一次死亡检测线程 pthread_create(&death_thread, NULL, check_death, phi); if (philo->n % 2 == 0) ft_usleep(philo->info->t_eat / 10); while (!is_dead(philo, 0)) { take_fork(philo); philo_eat(philo); if (philo->m_count == philo->info->n_eat) { pthread_mutex_lock(&philo->info->m_stop); if (++philo->info->philo_eat == philo->info->n_philo) { pthread_mutex_unlock(&philo->info->m_stop); is_dead(philo, 2); } pthread_mutex_unlock(&philo->info->m_stop); // 通知检测线程退出并等待资源回收 pthread_mutex_lock(&philo->info->dead); philo->info->stop = 1; pthread_mutex_unlock(&philo->info->dead); pthread_join(death_thread, NULL); return (NULL); } } // 等待检测线程结束 pthread_join(death_thread, NULL); return (NULL); } void *check_death(void *phi) { t_philo *philo; philo = (t_philo *)phi; // 循环检测直到退出条件触发 while (!is_dead(philo, 0)) { ft_usleep(1); // 短间隔轮询,保证检测及时性 pthread_mutex_lock(&philo->info->m_eat); pthread_mutex_lock(&philo->info->m_stop); if (!is_dead(philo, 0) && timestamp() - philo->last_eat >= (long)(philo->info->t_die)) { pthread_mutex_unlock(&philo->info->m_eat); pthread_mutex_unlock(&philo->info->m_stop); print(philo, " died\n"); is_dead(philo, 1); return (NULL); } pthread_mutex_unlock(&philo->info->m_eat); pthread_mutex_unlock(&philo->info->m_stop); } return (NULL); }
关键改进点
- 减少线程创建次数:每个哲学家仅初始化一个检测线程,避免频繁的线程资源分配/释放。
- 同步线程退出:用
pthread_join等待检测线程结束,确保线程资源被完全回收。 - 优化检测逻辑:短间隔循环替代单次长时间睡眠,既保证死亡检测的及时性,又避免线程资源长时间占用。
内容的提问来源于stack exchange,提问作者ambroiselebs
相关产品推荐
相关产品推荐

