Как программно прекратить чтение из стандартного ввода? - PullRequest
2 голосов
/ 15 ноября 2010

fgetc () и другие функции ввода могут возвращаться, когда в дескрипторе файла нет данных. Это может быть смоделировано для чтения консольных приложений из стандартного ввода с клавиатуры Ctrl-D (по крайней мере, в Unix). Но как это сделать программно? Например, как вернуться из функции fgetc () в потоке читателя в следующем коде (NB: игнорировать возможное условие гонки)?

#include <pthread.h>
#include <stdio.h>

void* reader()
{
    char read_char;
    while((read_char = fgetc(stdin)) != EOF) {
        ;
    }

    pthread_exit(NULL);
}

int main(void)
{
    pthread_t thread;
    pthread_create(&thread, NULL, reader, NULL);

    // Do something so the fgetc in the reader thread will return

    pthread_exit(NULL);
}

Спасибо! * * 1004

Ответы [ 4 ]

3 голосов
/ 15 ноября 2010

Кажется, вы хотите, чтобы потоки прекратили блокировать на fgetc(stdin), когда происходит какое-то событие для обработки этого события. Если это так, вы можете select() как для stdin, так и для другого канала сообщений, чтобы поток мог обрабатывать ввод из обоих:

fd_set descriptor_set
FD_ZERO(&descriptor_set); 
FD_SET(STDIN_FILENO, &descriptor_set); 
FD_SET(pipefd, &descriptor_set); 

if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0) 
{ 
  // select() error
} 

if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
  // read byte from stdin
  read(STDIN_FILENO, &c, 1);
}

if (FD_ISSET(pipefd, &descriptor_set)) 
  // Special event. Do something else

Также обратите внимание, что только один поток в вашем процессе должен читать из stdin.

1 голос
/ 15 ноября 2010

Вы можете либо «закрыть» стандартный ввод, либо подключить стандартный ввод к «/ dev / null» («NUL:» в Windows) с помощью freopen(), либо подключить стандартный ввод к «/ dev / zero».

Если вы закроете его, каждый вызов функции будет неудачным, поскольку поток файлов недопустим. Если вы подключите его к нулевому источнику данных, все чтения завершатся неудачно и немедленно вернут EOF. Если вы подключите его к нулевому источнику данных, каждое чтение будет успешным и вернет соответствующее количество нулевых байтов.

Возможно, один из них удовлетворит ваши потребности. Если нет, то вам, вероятно, нужно дать нам более подробное объяснение того, что вам действительно нужно.

0 голосов
/ 22 мая 2018

С POSIX вы можете сигнализировать потоку, чьи примитивы (например, «чтение») блокируются, и если вы установили обработчик сигнала «ничего не делать» с очищенным битом SA_RESTART, то примитивы завершатся с ошибками EINTR.Вот рабочая версия оригинала:

#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

static void SignalHandler(int signum)
{
}

void* reader(void *arg)
{
    char read_char;
    while((read_char = fgetc(stdin)) != EOF) {
        ;
    }
    printf("leaving reader\n");
    return NULL;
}

int main(int argc, const char * argv[])
{
    struct sigaction action;
    memset(&action, 0, sizeof(action));  // SA_RESTART bit not set
    action.sa_handler = SignalHandler;
    sigaction(SIGUSR1, &action, NULL);

    pthread_t thread;
    pthread_create(&thread, NULL, reader, NULL);

    sleep(1); // time to start reader thread
    // Do something so the fgetc in the reader thread will return
    pthread_kill(thread, SIGUSR1);
    sleep(1); // time to exit reader thread; could join it if set up

    return 0;
}
0 голосов
/ 15 ноября 2010

Александр разместил правильное решение.Его ответ точно отвечает на вопрос, который я задал.Далее следует простой самокомпилирующийся код, основанный на его подсказках:

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>

static int pipe_fds[2];

void* user_interaction()
{
    char read_char;

    fd_set descriptor_set;
    FD_ZERO(&descriptor_set); 
    FD_SET(STDIN_FILENO, &descriptor_set); 
    FD_SET(pipe_fds[0], &descriptor_set);

    while(1)
    {
        if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0) {
            // select() error
        }

        if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
            // read byte from stdin
            read(STDIN_FILENO, &read_char, 1);
        }

        if (FD_ISSET(pipe_fds[0], &descriptor_set))
            // Special event. break
            break;
    }

    pthread_exit(NULL);
}

int main(void)
{
    pipe(pipe_fds);

    pthread_t thread;
    pthread_create(&thread, NULL, user_interaction, NULL);

    // Before closing write pipe endpoint you are supposed
    // to do something useful
    sleep(5);

    close(pipe_fds[1]);

    pthread_join(thread, NULL);

    pthread_exit(NULL);
}
...