2016-11-01 2 views
1

Я пишу программу, которая захватывает видео с 4 камер одновременно, поэтому у меня есть 4 потока для управления каждой камерой. В каждом потоке я хочу, чтобы он продолжал захватывать, пока я не нажал клавишу, и этот ключ соответствует «q» или что-то еще.C++ handle keypress в многопоточной программе в Linux

Для нажатия клавиши ручки Я поиск в Интернете и найти способ, как это:

#include <stdio.h> 
#include <termios.h> 
#include <unistd.h> 
#include <fcntl.h> 

int kbhit(int key) { 
    int ch; 
    int old_file_status; 
    struct termios old_term_attr; 
    struct termios new_term_attr; 

    tcgetattr(STDIN_FILENO, &old_term_attr); 
    new_term_attr = old_term_attr; 
    new_term_attr.c_lflag &= ~(ICANON | ECHO); 
    tcsetattr(STDIN_FILENO, TCSANOW, &new_term_attr); 

    old_file_status = fcntl(STDIN_FILENO, F_GETFL, 0); 
    fcntl(STDIN_FILENO, F_SETFL, old_file_status | O_NONBLOCK); 

    ch = getchar(); 

    tcsetattr(STDIN_FILENO, TCSANOW, &old_term_attr); 
    fcntl(STDIN_FILENO, F_SETFL, old_file_status); 

    if(ch == c) 
     return 1; 
    return 0; 
} 

И в моем классе VideoCapture у меня есть такой код (не полный):

static void *capureVideo(void *para) { 
    // Some code... 
    while(!(kbhit('q') { 
     // Read frame... 
    } 
} 

void creatThread() { 
    if (pthread_create(&threadID, NULL, capureVideo, this) != 0) { 
     perror("thread create faild"); 
     exit(EXIT_FAILURE); 
    }  
} 

При запуске программы, , как только я нажимаю клавишу «q» 4 раза, программа заканчивается, и управление возвращается в оболочку. Но в некоторых определенных обстоятельствах (я точно не знаю, это не происходит каждый раз), это приводит к проблеме, то есть когда я затем перехожу и набираю команды в оболочку, символы, которые я печатаю, не отображаются. Когда я нажимаю, вводят команды.

Я ищу эту проблему и нашел следующее: https://askubuntu.com/a/172747, что указывает на то, что мои атрибуты терминала не были сброшены правильно. Но в коде рукописного ввода я заметил, что эти две строки кода

tcsetattr(STDIN_FILENO, TCSANOW, &old_term_attr); 
fcntl(STDIN_FILENO, F_SETFL, old_file_status); 

сбросил атрибуты терминала. Поэтому я задаюсь вопросом, относится ли это к многопоточному. Я новичок в программировании mutlithread и не могу решить его сам, так может кто-нибудь мне помочь? Любые предложения приветствуются.

+0

Сброс атрибутов терминала вручную не будет абсолютно ничего, если программа будет прервана CTRL-C или каким-либо другим сигналом. Помимо явного сброса атрибутов терминала до их значений по умолчанию, обработчик сигнала должен быть настроен с помощью 'sigaction()' для, по крайней мере, 'SIGINT',' SIGHUP', 'SIGTERM' и, возможно,' SIGQUIT', которые сбросит атрибуты терминала до значений по умолчанию. –

+0

Здесь также есть условие гонки, где один терминал сброса потока присваивает значение по умолчанию непосредственно перед тем, как другой поток входит в 'getchar()' и заканчивает блокировку стандартного ввода. Это, в общем, ошибочный подход. Терминал должен быть настроен на неблокирующий режим и неканонический режим обработки только один раз, в начале и сброс только до завершения программы. И вместо 'getchar()' use 'read()' в дескрипторе файла 0. –

+0

'Терминал должен быть настроен на неблокирующий режим и неканонический режим обработки только один раз, в начале и сброс только до того, как программа завершится . Значит, я могу использовать одноэлементный шаблон? Кроме того, не могли бы вы объяснить, почему 'getchar()' не рекомендуется. Большое спасибо. @SamVarshavchik – Gzy

ответ

0

У вас есть несколько потоков пытаются изменить параметры (статический) терминала в то же время:

tcgetattr(STDIN_FILENO, &old_term_attr); 
new_term_attr = old_term_attr; 
new_term_attr.c_lflag &= ~(ICANON | ECHO); 
tcsetattr(STDIN_FILENO, TCSANOW, &new_term_attr); 

Без этого быть заблокирован, ваши атрибуты терминала совершенно случайные. Исправления должны либо защитить это с помощью мьютекса, либо если вы создаете другой поток для чтения клавиатуры и установите флаг для нажатия клавиши «q»; которые ваши другие потоки могут читать, вы можете сделать что-то вроде

(pardon the psudo code) 
bool shouldRun = true 


void captureThreadMain { 
    while (shouldRun) { 
     captureFrame(); 
    } 
} 

void keyboardPressMain { 
    while (getKey('q')); 
    shouldRun = false; 
} 

Это будет означать, что вам нужно только нажать «д» один раз, чтобы остановить все рамку сбора нитей.

+0

Я создаю новый поток, чтобы прочитать клавиатуру и установить глобальную переменную 'bool shouldCapture', для меня это действительно сработало, спасибо! – Gzy

Смежные вопросы