У меня есть следующая функция, которая будет работать в потоке:
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_print (который является собственной реализацией, которая, как предполагается, работает так же, как printf) на исходный printf, он работает нормально. Почему? printf использует мьютекс перед печатью? как я могу исправить свой код?
Обновлено:
Ссылка на реализацию ft_printf, она слишком велика, чтобы помещать ее сюда.
Я добавил ссылку с реализацией
Ответ на ваш вопрос — да, printf() должен быть потокобезопасным, поэтому он должен использовать мьютекс. Прочтите справочную страницу.





Если я заменю ft_print на исходный printf, все будет работать нормально. Почему?
POSIX требует, чтобы printf был потокобезопасным.
Но последовательность операторов printf не атомарна, так что вам тоже повезло.
Вам необходимо убедиться, что группа вызовов printf выполняется атомарно. В этом случае лучшее решение — объединить их в один.
printf(
"\033[1;31m"
"%u %d %s\n"
"\033[0m",
time, id, msg
);
как я могу исправить свой код?
Как и в случае с printf, вам необходимо сделать группу вызовов атомарной. Да, для этого вы можете использовать мьютекс.
// Lock the mutex here.
ft_printf( "\033[1;31m" );
ft_printf( "%u %d %s\n", time, id, msg );
ft_printf( "\033[0m" );
// Unlock it here.
Обратите внимание, что простое помещение мьютекса вслепую в ft_printf не поможет — вам нужно определить, что нужно вывести атомарно, и убедиться, что несколько таких мьютексов не смешиваются.
@ Крис Додд, верно. Это должно быть вокруг группы вызовов. Это необходимо и для printf, как уже сказано в ответе. (Ну, это подразумевалось, но я просто сделал это более явным.)
Реальная разница, которую вы видите в printf, заключается в БУФЕРИЗАЦИИ, а не в синхронизации.
Printf (и функции stdio в целом) на самом деле просто записывают вывод в буфер и фактически ничего не выводят, пока буфер не будет очищен. По умолчанию это происходит всякий раз, когда выводится символ \n (новая строка) или когда буфер заполняется. Итак, с вашим кодом, если у вас было:
printf("\033[1;31m");
printf("%u %d %s\n", time, id, msg);
первая строка будет просто записываться в буфер, а вторая строка будет добавлена в буфер, а затем вся строка будет записана как один вызов. При использовании отдельных процессов это дает эффект атомарности, поскольку базовый вызов записи запишет строку на терминал как атомарную единицу.
Обратите внимание, что простое использование внутреннего мьютекса и выполнение каждого вызова ft_printfatomic не поможет, поскольку эти два вызова действительно необходимо объединить в атомарный блок. Использование мьютекса в каждом print_color для атомарности всего вызова будет работать.
Похоже, что ваш существующий код использует pthreads, поэтому простое использование printf (с общим буфером STDOUT и блокировкой мьютекса в каждом вызове printf) может показаться работающим, но все равно будут наблюдаться случайные сбои, если два потока попытаются записать строки разных цветов. одновременно.
Запись байтов в буфер по своей сути не является атомарной; printf() блокирует мьютекс для обеспечения атомарности.
@JeremyFriesner: да, но в данном случае это оказывается неадекватным (или очень актуальным).
Re «Это дает эффект атомарности». Нет, это не так. Буферизация вообще не имеет значения.
Нам нужно увидеть
ft_print, чтобы сказать, почему его поведение отличается отprintf.