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

使用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);
}

关键改进点

  1. 减少线程创建次数:每个哲学家仅初始化一个检测线程,避免频繁的线程资源分配/释放。
  2. 同步线程退出:用pthread_join等待检测线程结束,确保线程资源被完全回收。
  3. 优化检测逻辑:短间隔循环替代单次长时间睡眠,既保证死亡检测的及时性,又避免线程资源长时间占用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 10:34:55