簡體   English   中英

使用pthreads窺視stdin

[英]Peek stdin using pthreads

我正在嘗試偷看stdin,看看是否有使用pthreads的東西。 我(我想)需要這樣做,因為如果std in中什么都沒有,流訪問函數將阻塞輸入。

我認為,執行此操作的方法是觸發一個pthread,該pthread檢查stdin和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;
}    

您不需要pthread,可以使用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對象或stdin FILE結構中是否有數據是很糟糕的,因為正如Nemo所說,它們已被緩沖。 最好的辦法是在此示例中使用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 */
}

有關更多信息,請訪問http://linux.die.net/man/2/select上select(2)手冊頁。

PS:您可以嘗試依靠std::cin.rdbuf()->in_avail()返回的值,如cpluplus網站http://www.cplusplus.com/reference/iostream/streambuf/in_avail/中所述 ,甚至在istream類的readsome()方法中,但它們通常取決於FILE緩沖區,而該緩沖區不在GNU C ++庫中公開。 不要這樣做,否則您可能會失敗。

sleep(2)是毫無用處的,因為它不能保證您的線程將在程序終止前2微秒內完成。 您需要實現pthread_join(thread, NULL); 等待線程完成。 請參見此處,以獲得一個很好的pthread示例。

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

編輯:鮑(Bawh),費爾南多(Fernando)的忍者:)

好的,所以我不確定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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM