2011-07-27 25 views
2

我試圖偷看stdin,看看有沒有使用pthreads。我(認爲我)需要這樣做,因爲如果std中沒有任何內容,流訪問函數將阻止輸入。Peek stdin使用pthreads

我覺得做到這一點的方法是發射檢查標準輸入的pthread,然後sleep(1)並查看線程是否找到任何東西。

這是我到目前爲止。如果沒有任何東西被傳入程序,那麼它會按預期的方式睡眠,但如果某些東西在stdin中,則線程永遠不會被觸發。

#include <iostream> 
#include <pthread.h> 
#include <stdlib.h> 

using namespace std; 

void *checkStdIn(void *d){ 
    char c = '\0'; 
    c = cin.peek(); 
    if (c){ 
     cout << c << endl; 
    } 
} 

int main(int argc, char *argv[]){ 

    pthread_t thread; 
    int rc; 
    rc = pthread_create(&thread, NULL, checkStdIn, NULL); 
    if (rc){ 
     cerr << "Error no. " << rc << endl; 
     exit(EXIT_FAILURE); 
    } 
    sleep(2); 

    return 0; 
}  
+0

程序結束後才能執行線程。 – 2011-07-27 17:01:26

+0

爲什麼不睡覺()雖然補救? –

+0

你的線程檢查一次輸入,找不到並終止。 –

回答

4

你不需要pthreads,你可以使用select(2)poll(2)以瞭解是否可以在不阻止應用程序的情況下查看stdin。對於我的編碼功能my_peek()你只需要傳遞的要等待輸入秒(如果你不想等待,你甚至可以通過0)數量:

/* According to POSIX.1-2001 */ 
#include <sys/select.h> 

/* According to earlier standards */ 
#include <sys/time.h> 
#include <sys/types.h> 
#include <unistd.h> 

#include <iostream> 

int 
my_peek(unsigned int nsecs) 
{ 
    struct timeval timeout; 
    fd_set rfds; 
    int fd; 

    // stdin file descriptor is 0 
    fd = 0; 

    timeout.tv_sec = nsecs; 
    timeout.tv_usec = 0; 

    FD_ZERO(&rfds); 
    FD_SET(fd, &rfds); 

    if (select(fd + 1, &rfds, NULL, NULL, &timeout) > 0) 
     return std::cin.peek(); 
    return -1; 
} 

int 
main(void) 
{ 
    int peek; 

    peek = my_peek(2); 
    if (peek != -1) { 
     std::cout << "we could peek without freezing" << std::endl; 
     std::cout << "my_peek() returned " << peek << std::endl; 
    } else { 
     std::cout << "we could not peek without freezing" << std::endl; 
    } 

    return 0; 
} 

請注意,依靠select(2)來判斷cin對象或stdinFILE結構中是否有數據是不好的,因爲正如Nemo所說,它們是BUFFERED。最好的辦法是在本例中使用read(2)避免「cin」。的my_peek()改進的版本會是什麼樣子:

int 
my_peek(unsigned int nsecs) 
{ 
    struct timeval timeout; 
    fd_set rfds; 
    int fd; 
    unsigned char c; 

    // stdin file descriptor is 0 
    fd = 0; 

    timeout.tv_sec = nsecs; 
    timeout.tv_usec = 0; 

    FD_ZERO(&rfds); 
    FD_SET(fd, &rfds); 

    if (select(fd + 1, &rfds, NULL, NULL, &timeout) <= 0) 
     return -1; 
    if (read(fd, &c, 1) != 1) 
     return -1; 
    return static_cast<int>(c); /* or "return (int)c" for C-only programs */ 
} 

欲瞭解更多信息,請檢查select(2)手冊頁在http://linux.die.net/man/2/select

PS:你可以嘗試依靠由std::cin.rdbuf()->in_avail()返回的值,或者甚至在istream類的readsome()方法在cpluplus網站http://www.cplusplus.com/reference/iostream/streambuf/in_avail/解釋,但他們通常依賴於FILE緩衝未暴露GNU C++庫。不要這樣做,否則你可能會失敗。

+1

如果您使用的是支持POSIX的平臺,那麼這是迄今爲止支持pthreads的環境的最佳答案。當操作系統可以告訴你文件描述符的狀態時,不需要產生線程。 (+1) –

+1

除了'std :: cin'通常被緩衝,所以這通常不起作用。 (嘗試偷看+一次讀取一個字符的循環,重定向stdin從一個文件,你會明白我的意思。) – Nemo

+0

謝謝你,尼莫。我修正了這個例子,並添加了一些技巧來避免緩衝區問題 –

0

sleep(2)是毫無價值的,因爲它並不能保證你的線程將編程終止前2微秒內完成。您需要實施pthread_join(thread, NULL);以等待線程完成。在這裏看到一個好例子pthread

此外,cin.peek()將阻止等待輸入。這就是它的設計。請參閱此處的cin.peek的示例。

+0

「睡眠」時間實際上是整秒,但無論如何都需要加入。 –

0

編輯: Bawh,費爾南多ninja'd :)

好了,所以我在最好的答案會是什麼你,因爲我不能告訴你想要什麼不知道100%的程序最終

首先,這不是應該使用線程解決的問題。線程不是一切萬能的解決方案,並且與其他解決方案相比,通常會有巨大的開銷。由於您已經在使用pthreads,因此我認爲Windows兼容性不是問題。

第一步是禁用規範模式,這將允許您無需等待enter即可獲取字符。如果你100%肯定stdin永遠不會是一個終端,你可以跳過這一步。

#include <iostream> 
#include <termios.h> 

void toggle_canonical(bool on) { 
    struct termios terminal_settings; 

    // Get the existing terminal settings 
    tcgetattr(0 /* stdin */, &terminal_settings); 
    if(on) { 
     // Disable canonical mode 
     terminal_settings.c_lflag &= ~ICANON; 
     // Read at least one character. 
     terminal_settings.c_cc[VMIN] = 1; 
    } else { 
     // Enable canonical mode 
     terminal_settings.c_lflag |= ICANON; 
    } 
    tcsetattr(0 /* stdin */, TCSANOW, &terminal_settings); 

    return; 
} 

int main(const int argc, const char* argv[]) { 
    // The read timeout, which can be 0 if you don't want to block at all. 
    struct timeval to = {5 /* seconds */, 0 /* miliseconds */}; 
    fd_set read_fds; 
    int ret = 0; 

    // Turn canonical mode on 
    toggle_canonical(true); 

    FD_ZERO(&read_fds); 
    FD_SET(0, &read_fds); 

    // The first parameter to select() is the highest file 
    // descriptor in the set + 1, so in this case because 
    // STDIN == 0, we pass 1. This is actually completely 
    // ignored on several platforms, including Windows. 
    if((ret = select(1, &read_fds /* read set */, NULL /* write set */, NULL /* error set */, &to /* timeout */)) == 0) { 
     std::cout << "You didn't type anything in time." << std::endl; 
    } else if (ret == 1) { 
     std::cout << "Yay, you typed something in time!" << std::endl; 
    } else if (ret == -1) { 
     std::cout << "Oh no, an error occured!" << std::endl; 
    } 

    // Turn canonical mode off 
    toggle_canonical(false); 
    return 0; 
}