2016-11-01 50 views
1

我寫了一個程序,它可以同時從4個攝像頭捕獲視頻,所以我有4個線程來控制每個攝像頭。在每個線程中,我希望它繼續捕獲,直到我點擊一個鍵,並且該鍵對應於'q'或某物。C++在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次程序退出並且控制回到shell。但在某些特定情況下(我不完全知道,它不會每次都發生)導致出現問題,也就是說,當我隨後將命令輸入到shell中時,我輸入的字符未顯示出來。當我按下輸入命令提交。

我搜索這個問題,發現這個:https://askubuntu.com/a/172747,這表明我的終端屬性沒有正確重置。但在按鍵處理代碼中我注意到這兩行代碼

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

確實重置了終端屬性。所以我想知道它是否與多線程相關。我是多線程編程的新手,無法自己解決,所以有人可以幫助我嗎?任何建議,高度讚賞。

+0

如果程序以CTRL-C或其他信號終止,手動重置終端屬性將完全沒有任何作用。除了明確地將終端屬性重置爲默認值以外,還應該通過sigaction()至少爲SIGINT,SIGHUP,SIGTERM和SIGQUIT建立一個信號處理程序,會將終端屬性重置爲其默認值。 –

+0

這裏還有一個競爭條件,其中一個線程重置終端在不同線程進入'getchar()'之前屬性爲默認值,並最終在標準輸入上阻塞。總的來說,這是一種錯誤的方法。終端應該只在開始時設置爲非阻塞模式和非規範處理模式一次,並且僅在程序終止之前復位。而不是'getchar()'在文件描述符0處使用'read()'。 –

+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; 
} 

這將意味着你只需要按下「Q」一旦停止所有的框回收線程。

+0

我產生了一個新的線程來讀取鍵盤和設置一個全局變量'bool shouldCapture',對我來說它確實工作,謝謝! – Gzy