Pthread зависает на стандартном выходе?

Допустим, в программе на 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 (спящий и printf), вы увидите, что он также использует 100% ресурсов ЦП. Но хт

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


person hhy    schedule 02.11.2016    source источник
comment
К сожалению не могу воспроизвести проблему.   -  person johnchen902    schedule 03.11.2016
comment
@ johnchen902 Может быть, это связано с ОС? Я использую Ubuntu14.04-LTS. И проблема не появится, если приоритет потока не установлен. Например, если я запускаю его без sudo, все работает просто отлично.   -  person hhy    schedule 03.11.2016
comment
Проблема решена. Смотрите мой комментарий под ответом Тава.   -  person hhy    schedule 03.11.2016
comment
@hhy - Как я прокомментировал ниже, я не думаю, что это решение. Судя по вашему коду, вывод на stdout не должен быть заблокирован. Я не уверен, почему за вас проголосовали, потому что это кажется реальной проблемой.   -  person Andrew Henle    schedule 03.11.2016


Ответы (1)


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

person Tav    schedule 03.11.2016
comment
Ну вообще ты эксперт! Если я закомментирую все printf в дочернем потоке, проблема исчезнет. Предположим, что цикл while в дочернем потоке не позволяет другим потокам получить доступ к стандартному выводу, если printf вызывается перед циклом while. - person hhy; 03.11.2016
comment
@hhy — комментирование printf в дочернем потоке не должно влиять на результат. Если дочерний поток удерживает блокировку stdout после возврата printf, я считаю это ошибкой в ​​реализации. - person Andrew Henle; 03.11.2016