Просмотр stdin с помощью pthreads - PullRequest
2 голосов
/ 27 июля 2011

Я пытаюсь заглянуть в stdin, чтобы узнать, есть ли что-нибудь с помощью pthreads.Мне (кажется, мне) нужно это сделать, потому что если в std нет ничего, функции доступа к потоку будут блокироваться для ввода.

Я чувствую, что способ сделать это состоит в том, чтобы запустить pthread, который проверяет stdin, и уснуть (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;
}    

Ответы [ 3 ]

4 голосов
/ 27 июля 2011

Для этого вам не нужны 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 или stdin FILE, потому что, как сказал Немо, они БУФЕРЫ. Лучшее, что можно сделать, это избежать использования cin в этом примере, используя read(2). Улучшенная версия 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(), как описано на сайте cpluplus http://www.cplusplus.com/reference/iostream/streambuf/in_avail/, или даже в методе readsome() класса istream, но они обычно зависят в буфере FILE, который не доступен в библиотеке GNU C ++. Не делайте этого, иначе вы можете потерпеть неудачу.

0 голосов
/ 27 июля 2011

Редактировать: Вау, ниндзя от Фернандоскажите, что вы хотите, чтобы ваша программа в конечном итоге сделала .

Прежде всего, это не та проблема, которую следует решать с помощью потоков.Потоки не являются универсальным решением для всего и обычно имеют огромные накладные расходы по сравнению с другими решениями.Поскольку вы уже используете 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;
}
0 голосов
/ 27 июля 2011

Ваш sleep(2) бесполезен, потому что он не гарантирует, что ваш поток завершит работу за 2 микросекунды до завершения программы.Вам нужно реализовать pthread_join(thread, NULL);, чтобы дождаться окончания потока.Смотрите здесь хороший пример pthread .

Также cin.peek () заблокирует ожидание ввода.Вот как это устроено.Смотрите здесь пример cin.peek .

...