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

C语言多线程中自定义ft_printf引发竞态问题的原因及解决

多线程环境下自定义printf输出异常的原因及修复方案

问题代码

线程处理函数

void    *dinning_handler(void *arg)
{
    t_philo         *philo;
    struct timeval  start;

    philo = (t_philo *)arg;
    gettimeofday(&start, NULL);
    philo->last_meal_time = start;
    while (philo->max_eats == -1 || philo->eats < philo->max_eats)
    {
        print_blue("is thinking", philo->id, get_ts_in_ms());
        pthread_mutex_lock(philo->left_fork);
        pthread_mutex_lock(philo->right_fork);
        print_blue("has taken a fork", philo->id, get_ts_in_ms());
        print_green("is eating", philo->id, get_ts_in_ms());
        usleep(philo->time_to_eat * 1000);
        philo->eats++;
        gettimeofday(&philo->last_meal_time, NULL);
        pthread_mutex_unlock(philo->left_fork);
        pthread_mutex_unlock(philo->right_fork);
        print_blue("is sleeping", philo->id, get_ts_in_ms());
        usleep(philo->time_to_sleep * 1000);
    }
    return (NULL);
}

自定义打印函数示例

void    print_red(char **msg, int id, long time)
{
    ft_printf("\033[1;31m");
    ft_printf("%u %d %s\n", time, id, msg);
    ft_printf("\033[0m");
}

问题描述

上述多线程代码运行时会出现竞态条件,导致终端输出内容错乱,但将自定义的ft_printf替换为标准printf后输出恢复正常。需要解答:

  1. 出现该现象的原因是什么?
  2. 标准printf内部是否会使用互斥锁?
  3. 如何修复自定义代码的问题?

解答

1. 自定义ft_printf输出错乱的原因

自定义打印函数中,一次完整的输出被拆分成3次ft_printf调用:先输出颜色控制码、再输出核心内容、最后恢复默认颜色。在多线程环境下,这三次调用不是原子操作——线程A执行完第一次ft_printf输出颜色后,可能被线程B抢占CPU,线程B的输出会插入到线程A的颜色控制和内容之间,导致颜色错乱、内容混杂,最终出现输出异常。且你的ft_printf实现本身没有线程同步机制,无法保证多线程调用时的输出顺序和完整性。

2. 标准printf的线程安全性

在**POSIX兼容的系统(如Linux、macOS)**中,当stdout指向终端设备时,标准printf是线程安全的:内部会通过互斥锁或其他同步机制,保证一次printf调用的所有输出操作是原子的,不会被其他线程的printf打断。因此多个线程同时调用printf时,输出内容不会混杂。

注意:如果stdout指向文件、管道等非终端设备,printf的线程安全性可能会变化(取决于系统实现),但终端场景下的线程安全是标准保证的特性。

3. 代码修复方案

方案一:给打印操作添加全局互斥锁

这是最直接可靠的方案:

  • 定义全局互斥锁变量:pthread_mutex_t print_mutex;
  • 在创建线程前初始化锁:pthread_mutex_init(&print_mutex, NULL);
  • 修改所有自定义打印函数,在执行输出前加锁,所有输出完成后解锁:
void    print_red(char *msg, int id, long time)
{
    pthread_mutex_lock(&print_mutex);
    ft_printf("\033[1;31m");
    ft_printf("%u %d %s\n", time, id, msg);
    ft_printf("\033[0m");
    pthread_mutex_unlock(&print_mutex);
}
  • 所有线程退出后销毁锁:pthread_mutex_destroy(&print_mutex);

这样能保证每个打印函数的整个输出流程是原子的,不会被其他线程打断。

方案二:重构ft_printf实现

将一次完整的输出内容(包括颜色控制码、核心内容、恢复码)合并成一个字符串,再通过一次ft_printf调用输出。但这种方式需要处理字符串拼接的内存管理,且如果ft_printf内部本身有线程不安全的操作,仍可能出问题,因此不如加锁方案直接。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 10:55:30