Запуск программы на C с несколькими потоками в фоновом режиме, когда требуется ввод пользователя - PullRequest
7 голосов
/ 10 февраля 2011

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

Из любопытства я решил, что хотел бы, чтобы программа запустила вычисления, а затем позволила пользователю ввести символ, чтобы увидеть, как далеко продвинулись вычисления. Таким образом, в этом случае, если программа вычисляет n-й член последовательности Фибоначчи, и это еще не сделано, ввод числа «1» заставит программу вывести термин k, который она вычисляет в настоящее время. Я попытался сделать это, используя следующий подход.

Программа использует scanf для получения длинного целого числа, которое представляет член последовательности, который необходимо вычислить. Затем программа создает поток с подпрограммой, которую я написал, чтобы вычислить и напечатать n-й термин Фибоначчи, который завершается, как только он заканчивает это делать. После этого программа создает целочисленную переменную, которую я использовал для хранения входных данных, и инициализирует ее, чтобы получить ненулевое значение. Затем он непрерывно входит в цикл while, пока int не равен нулю, и на каждой итерации цикла он выполняет scanf ("% d", & i). Затем он сравнивает это значение с 1, и, если оно равно 1, он выводит значение счетчика, который я настроил для отслеживания хода вычислений Фибоначчи.

В любом случае, все вышеперечисленное работает очень плавно, несмотря на то, что я ужасно не в курсе таких вещей, как нити. Однако проблема, с которой я сталкиваюсь, заключается в том, что когда мне нужно вычислить, скажем, миллионный член последовательности, программе требуется несколько минут, и было бы неплохо просто запустить его в фоновом режиме. Однако, если я поместил процесс в фоновом режиме с помощью ctrl + z, а затем набрал bg, процесс запустится, но сразу остановится снова. Я предполагаю, что это потому, что он постоянно требует ввода от пользователя и поэтому останавливается, пока не получит этот ввод.

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

Извиняюсь за довольно скучный вопрос, и заранее благодарю за любую помощь!

Phil

Редактировать: По запросу я добавил (очень упрощенная версия) код здесь. Основная идея та же: программа запускает длительные вычисления в новом потоке, а затем выполняет цикл для ввода с помощью scanf. Ввод 0 завершает работу программы, ввод 1 отображает счетчик, показывающий ход вычислений. Я хотел бы иметь возможность запускать программу в фоновом режиме, но, поскольку он постоянно запрашивает ввод, он немедленно останавливает процесс. Игнорировать арифметическое переполнение на счетчике; моя настоящая программа имеет структуры данных, чтобы справляться с подобными вещами, но я старался максимально упростить свой код для удобства чтения.

//Simple program to test threading and input.
#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 2

void *stuff();
int counter; //keeps track of the progress of the computation

int main(){
  counter=1;
  pthread_t threads[NUM_THREADS];
  pthread_create(&threads[0], NULL, stuff, NULL);

  //loop while the input is non-zero so that the program can
  //accept commands
  int input=10;
  while(input){
    input=10;
    printf("Enter 0 to exit or 1 to display progress: ");
    scanf("%d", &input);
    if(input==1){
      printf("Currently iterating for the %dth time.\n", counter);
    }
  }

return 0;
}

//Randomly chosen computation that takes a while.
void *stuff(){
  long i,j,n=1000000000;
  for(i=0; i<=n; i++){
    for(j=0; j<=n; j++){
      i*i*i*i*i*i*i*i*i*i*i;
      j*j*j*j*j*j*j*j*j*j*j;
      counter++;
    }
  }
  printf("Done.\n");
  pthread_exit(NULL);
}

Ответы [ 4 ]

1 голос
/ 16 октября 2011

Вы можете предотвратить остановку программы, когда она пытается читать с терминала, в то время как фоновая, блокируя или обрабатывая SIGTTIN (и аналогично SIGTTOU для записи в терминал).

Когда ваша программа работает в фоновом режиме, SIGTTIN и SIGTTOU доставляются, когда она читает или пишет в терминал соответственно. По умолчанию ваша программа останавливается при получении этих сигналов.

Следующий бит кодовых блоков SIGTTIN и SIGTTOU с использованием pthread_sigmask() (поскольку ваша программа использует потоки - в противном случае можно использовать sigprocmask()):

#include <signal.h>

/* ... */

sigset_t sset;
sigemptyset(&sset);
sigaddset(&sset,SIGTTIN);
sigaddset(&sset,SIGTTOU);
pthread_sigmask(SIGBLOCK,&sset,NULL); /* should check return for errors..
                                           returns 0 on success */

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

1 голос
/ 11 августа 2011

Я бы предложил другой подход:

У меня есть программа, которая обрабатывает гигабайты данных, и я хочу увидеть, где я нахожусь в процессе.Это требует большого блока данных для печати, поэтому я хочу сделать это только по запросу.Я добавил обработчик сигнала к SIGTSTP (нажатие клавиши control-z отправляет этот сигнал), которое распечатывает данные.Для этого не требуется отдельный поток, он прост в реализации, а также позволяет сигнализировать об этом, если вы запускаете процесс в другом месте (поскольку вы можете просто отправить сигнал через kill)

1 голос
/ 11 августа 2011

Основная проблема заключается в том, что как только вы пытаетесь читать со стандартного ввода, когда вы находитесь в фоновом режиме, вы получаете SIGSTOP, что означает, что вы как будто сразу же нажали ctrl-z. Если вам нужно работать в фоновом режиме, сравнительно простое изменение здесь заключается в чтении из fifo вместо чтения из stdin. Используйте команду mkfifo для создания хорошо известного fifo, например, так:

mkfifo fib.fifo

Изменение вашего основного цикла следующим образом:


 int main(){
  int intput = 10;
  FILE * handle = fopen("fib.fifo", "r");
  if(!handle){
    //do something error  }
  while(1){
    //you might want to use fgets instead of fscanf
    fscanf(handle, "%d", &input);
    //now do whatever with input
  }


Чтения будут блокироваться, пока что-то не будет записано. Вы должны быть осторожны, если планируете запускать несколько экземпляров одновременно. Как только предмет прочитан с fifo, он исчез. Только один экземпляр увидит значение, которое вы написали. Вы можете записать в файл просто как "echo 1 >> fib.fifo"

1 голос
/ 10 февраля 2011

Предполагая, что вы используете потоки POSIX, вы можете scanf в одном потоке, позволить ему блокироваться до тех пор, пока что-то не введено, а затем pthread_cond_signal другому потоку делать то, что вы хотите.Вы также можете объявить переменную, которая обновляется вычислительным потоком и читается потоком с scanf в нем.Другой более сложный способ - прослушивать сокет на предмет входящих сообщений и иметь часть интерпретатора сообщений, которая читает из этого сокета и записывает результаты.В этом случае вам не нужен scanf, и ваша программа может работать в фоновом режиме.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...