2016-11-02 2 views
1

Скажем, там уже две нити (PTHREAD) в C++ программе:Pthread freezes stdout?

  • main thread
  • child thread

Что делает программа проста:

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

Проблема заключается в том, что после создания дочернего потока он замораживает stdout и больше ничего не печатается на консоли. Однако, когда программа выходит, все сообщения неожиданно отображаются на консоли. Ниже приведен .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% процессора. Но ht

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

+0

К сожалению, я не могу воспроизвести проблему. – johnchen902

+0

@ johnchen902 Возможно, это ОС? Я использую Ubuntu14.04-LTS. И проблема не появится, если приоритет потока не задан. Например, если я запускаю его без 'sudo', все работает отлично. – hhy

+0

Проблема решена. См. Мой комментарий под ответом Тава. – hhy

ответ

1

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

+0

На самом деле вы эксперт! Если я прокомментирую все 'printf' в дочернем потоке, проблема исчезнет. Угадайте, что цикл while в дочернем потоке останавливает другие потоки от доступа к stdout, если 'printf' вызывается перед циклом while. – hhy

+0

@hhy - Замечание «printf» в дочернем потоке не должно влиять на результат. Если дочерний поток удерживает блокировку на 'stdout' * после того, как' printf' возвращает *, я бы счел, что ошибка в реализации. –