Что замораживает стандартный вывод?

Допустим, в программе на C ++ есть два потока (pthread):

  • main thread
  • child thread

То, что делает программа, просто:

  1. Свяжите две темы с двумя разными ядрами.
  2. Установите приоритеты двух потоков на очень высокие значения (-99 для child thread и -98 для main thread).
  3. child thread выполняет какую-то тяжелую задачу, которая использует 100% процессора.
  4. main thread пытается вызвать printf () после child thread создано.

Проблема в том, что после создания дочернего потока он замораживает стандартный вывод и на консоли больше ничего не печатается. Однако при выходе из программы все сообщения неожиданно отображаются в консоли. Ниже приведен файл .cpp, демонстрирующий этот эффект:

main.cpp:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/mman.h>

bool EXIT = false;

void signal_handler(int signal){
EXIT = true;
}

void *child_thread(void *x_args){
printf("Setting child thread CPU affinity (Core #1)...\n");
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)){
perror("Cannot set child thread CPU affinity");
printf("Exit\n");
exit(1);
}

printf("Locking memory of child thread...\n");
mlockall(MCL_CURRENT | MCL_FUTURE);

printf("Setting child thread priority (-99)...\n");
struct sched_param sched_param;
sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO)-1;
if (sched_setscheduler(0, SCHED_FIFO, &sched_param)){
perror("Cannot set child thread priority");
printf("Exit\n");
exit(1);
}

printf("Entering while loop inside child thread...\n");
while(!EXIT){}
return NULL;
}

int main(){
signal(SIGINT, signal_handler);

pthread_t thread;

printf("Setting main thread CPU affinity (Core #0)...\n");
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
if (pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset)){
perror("Cannot set main thread CPU affinity");
printf("Exit.\n");
exit(1);
}

printf("Locking memory of main thread...\n");
mlockall(MCL_CURRENT | MCL_FUTURE);

printf("Setting main thread priority (-98)...\n");
struct sched_param sched_param;
sched_param.sched_priority = sched_get_priority_max(SCHED_FIFO)-2;
if (sched_setscheduler(0, SCHED_FIFO, &sched_param)){
perror("Cannot set main thread priority");
printf("Exit.\n");
exit(1);
}

printf("Creating child thread...\n");
if (pthread_create(&thread, NULL, child_thread, NULL)){
perror("Cannot create child thread");
printf("Exit.\n");
exit(1);
}

printf("Entering while loop in main thread...\n");
while(!EXIT){
sleep(1);
printf("I can't see this until I press Ctrl+C!\n");
}
pthread_join(thread, NULL);

printf("Exit.\n");
return 0;
}

Вы можете скомпилировать это с:

g++ main.cpp -pthread -o main

Затем запустите его с:

sudo ./main

Затем вы должны увидеть, что stdout зависает после вывода следующего:

Setting main thread CPU affinity (Core #0)...
Locking memory of main thread...
Setting main thread priority (-98)...
Creating child thread...
Entering while loop in main thread...
Setting child thread CPU affinity (Core #1)...

Даже через час вы просто не увидите результатов. Но когда Ctrl+C нажата. Вы увидите все сообщения, выходящие:

I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!
I can't see this until I press Ctrl+C!

main thread на самом деле работает в фоновом режиме, потому что если вы закомментируете две строки внутри цикла while (sleep и printf), вы увидите, что он также использует 100% CPU. Но хт

Что мне здесь не хватает?

1

Решение

Я не буду претендовать на звание эксперта, но у вас, кажется, есть один ресурс, стандартный вывод и два потока, пытающиеся его использовать. Я полагаю, что printf безопасен для многопоточности, но не реентент.
Моим первым инстинктом было бы использовать какую-то многопоточную блокировку доступа к printf и stdout, чтобы гарантировать, что только один поток вызывает его одновременно.

1

Другие решения

Других решений пока нет …