Прерывание потока программы в C - PullRequest
1 голос
/ 01 августа 2009

У меня работает цикл, который увеличивает значение переменной на каждой итерации, и я хочу иметь возможность нажимать клавишу на клавиатуре, чтобы остановить цикл и сообщить окончательное значение переменной. Дело в том, что я не могу понять, как это сделать на C. Я чувствую себя глупо, потому что мне кажется, что я упускаю из виду какой-то очень простой и легкий способ сделать это, но все, что я пытаюсь, останавливает цикл до * 1002. * Я нажал клавишу на клавиатуре, которая, по сути, является полной противоположностью того, что я хочу.

По сути, я хочу сделать что-то вроде этого:

 <code>
   while (key is not pressed)<br>
      increment value<br>
   print final value<br>

Имеет ли это смысл? В любом случае, какие-либо советы о том, как это сделать в C?

Ответы [ 11 ]

7 голосов
/ 01 августа 2009

Если вы пытаетесь читать по одному символу за раз (не нажимая Enter) с терминала в Linux, вам нужно будет установить терминал для небуферизованного ввода.

См. Этот пример:

Небуферизованный getc (3) под GNU / Linux

2 голосов
/ 01 августа 2009

Я думаю, что нет тривиального решения, взгляните на этот связанный вопрос

обнаружение события клавиатуры в C

2 голосов
/ 01 августа 2009

Если вам разрешено указывать, что нажимается, и вы работаете в POSIX.1-совместимой системе, вы можете настроить обработчик сигнала для перехвата SIGINT (отправляется Ctrl + C). Пусть ваш обработчик изменит значение переменной так, чтобы вы выпали из цикла while.

Если вы выберете этот подход, будьте осторожны. Если неправильная реализация приводит к бесконечному циклу и перехвату SIGINT, вы не сможете завершить свою программу с помощью Ctrl + C. В этом случае вам нужно будет использовать kill (1) для завершения вашей программы.

2 голосов
/ 01 августа 2009

Если вы работаете с Windows и используете MSVC, вам могут понадобиться getch () и kbhit (), что-то вроде этого

#include <conio.h>

while( looping ) {

    // do regular loop stuff

    // check if a key is hit, w/o blocking, using kbhit()
    if( kbhit() ) {
        // only runs when user has hit a key
        // so display stuff here,
        // and wait for permission to resume with getch()
        getch();
    }
}
1 голос
/ 01 августа 2009

Это зависит от вашей платформы. Язык Си не определяет такие вещи, как этот.

Windows? линукс? (gnome app? kde app? терминал?) что-то еще?

0 голосов
/ 24 ноября 2017

Некоторые люди упоминают о блокировке. В средах * nix вы можете попробовать установить стандартный дескриптор входного файла в неблокирующий режим. Вот пример:

/*  testbrk

  Test breaking an infinite loop with a keystroke.
  Hit the <Enter> key to break this loop.

*/


/* Include block */
#include  <errno.h>
#include  <fcntl.h>
#include  <stdio.h>
#include  <stdlib.h>
#include  <unistd.h>


/*---testbrk main loop---*/

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

  /* Variables: file flags , return value , buffer */
  int  f ;
  ssize_t  r ;
  char  b ;

  /*
    Switch standard input to non-blocking mode:
    -Get existing flags from the file descriptor
    -Bitwise OR to activate non-blocking bit
    -Apply new flags
  */
  if  ( f = fcntl( STDIN_FILENO , F_GETFL , 0 )  ==  -1 )
  {
    perror ( "fcntl F_GETFL" ) ;
    exit ( EXIT_FAILURE ) ;
  }

  f |= O_NONBLOCK ;

  if  ( f = fcntl( STDIN_FILENO , F_SETFL , f )  ==  -1 )
  {
    perror ( "fcntl F_SETFL" ) ;
    exit ( EXIT_FAILURE ) ;
  }


  /*
    Infinite loop
    -try reading from standard input directly via its file
     descriptor , NOT the standard input stream
    -if the number of bytes returned is > zero then break
    -if bytes returned is -1 then check errno for EAGAIN,
     meaning that read would block but the file is nonblocking
    -if zero is returned then print message immediately
  */
  while  ( (r = read( STDIN_FILENO , &b , sizeof( b ) ))  <  1 )
  { 

    if  ( r == -1  &&  errno != EAGAIN )
    {
      perror ( "read" ) ;
      exit ( EXIT_FAILURE ) ;
    }

    fprintf ( stderr , "\nInfinite loop" ) ;

  } /* inf loop */

  /* Done */
  printf ( "\n\nBroken loop\n\n" ) ;
  exit ( EXIT_SUCCESS ) ;

} /* testbrk */
0 голосов
/ 01 августа 2009

Я попытаюсь взглянуть не на дословный вопрос, который вы задаете, а на ваше намерение - очевидно, вы хотите представить результат вычислений, вызванный какой-то реакцией пользователя.

Проверка «нажата ли клавиша?» на каждой итерации достаточно расточительно - ваш процессор может тратить все это время на выполнение более полезных задач.

Итак, лучший подход здесь - это использование сигналов, на мой взгляд, а именно, SIGINT, который срабатывает, когда вы нажимаете «Ctrl-C». Вот код, который напечатает значение переменной, когда вы нажмете Ctrl-C, и выйдет после того, как вы сделаете это три раза:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>

long globalvar = 0;
int interrupts_before_exit = 3;

void ctrl_c_handler(int x) {
  printf("Value of the variable: %ld\n", globalvar);
  if(--interrupts_before_exit) {
    printf("Press Ctrl-C %d more times to stop\n", interrupts_before_exit);
  } else {
    printf("Computation interrupted!\n");
    exit(0);
  }
}


int main(int argc, char *argv[]) {
  struct sigaction act;

  act.sa_handler = ctrl_c_handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;

  if(sigaction(SIGINT, &act, NULL) >= 0) {
    while (1) {
      /* The work happens here */
      globalvar++; 
    }
  }
  exit(1);
}

Вы можете передавать сигналы между программами, так что на самом деле вы можете форкнуть программу, которая выполняет «работу», и тогда вторая программа будет лениво контролировать клавиатуру - и, как только будет нажата клавиша, она отправит сигнал на первый, который будет печатать результат.

0 голосов
/ 01 августа 2009

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

Вы можете посмотреть на ссылку http://www.gnu.org/s/libc/manual/html_node/Waiting-for-I_002fO.html для примера использования оператора select с сокетами.

Я изменил этот пример, чтобы использовать STDIN в качестве дескриптора файла. Функция вернет 0, если нет ожидающего ввода, 1, если есть ожидающий ввод (т.е. кто-то нажал клавишу на клавиатуре ввода), или -1, если произошла ошибка какого-либо характера

 #include <errno.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/types.h>
 #include <sys/time.h>

 int waitForKBInput(unsigned int seconds)
 {
   /* File descriptor set on which to wait */
   fd_set set;
   /* time structure which indicate the amount of time to
      wait.  0 will perform a poll */
   struct timeval timeout;

   /* Initialize the file descriptor set. */
   FD_ZERO (&set);
   /* Use the Standard Input as the descriptor on which 
      to wait */
   FD_SET (STDIN, &set);

   /* Initialize the timeout data structure. */
   timeout.tv_sec = seconds;
   timeout.tv_usec = 0;

   /* select returns 0 if timeout, 1 if input available, -1 if error. */
   /* and is only waiting on the input selection */
   return select (FD_SETSIZE,
                  &set, NULL, NULL,
                  &timeout));
 }

Я знаю, что это не будет работать в системе VMS, так как я попробовал это, и они реализовали Select и STDIN по-разному, чтобы он не работал (пришлось использовать другие средства для обнаружения ввода с клавиатуры).

Для Visual C / C ++ можно было бы использовать функцию kbhit, которая указала бы, есть ли ввод с клавиатуры для чтения.

0 голосов
/ 01 августа 2009

Это единственное место, где стандарт C оставляет программистов зависать, чтобы высохнуть. Наиболее переносимым решением этой проблемы является выполнение ввода-вывода с использованием библиотеки curses , которая обрабатывает так называемый «необработанный» ввод с клавиатуры (что вам нужно) плюс намного больше. Кривая обучения немного крутая, но есть хорошие учебники, особенно в документации программистов BSD.

0 голосов
/ 01 августа 2009

Зачем считать в цикле, пока кто-нибудь не нажмет клавишу, имеет смысл?

Если вы действительно хотите чего-то подобного, проверьте время примерно на 10 тыс. Итераций. Затем сделайте ожидание в не блокирующем итерации со сном (). Когда вы получите время, вы можете использовать его для приблизительного определения окончательного значения

.
...