开发者

Running a C program with multiple threads in the background when it requires user input

I've made a fairly simple C program to compute the terms of Fibonacci sequence, which I'm running on Ubuntu. I made some rather clumsy data structures so that it can do very large integers, but the specifics of the program aren't terribly important -- what matters is that the program can take quite a while to perform the computations.

Out of curiosity, I decided I'd like to make the program start the computation and then allow the user to input a character to see how far along the computation is. So in this case, if the program is computing the n'th term of the Fibonacci sequence and it isn't done yet, inputting the number '1' will get the program to output the term k that it is currently computing. I've attempted to do that using the following approach.

The program uses scanf to get a long integer which represents the term of the sequence that needs to be computed. The program then creates a thread with the routine that I wrote to compute and print the n'th Fibonacci term, which exits once it has finished doing that. Following that, the program creates an integer variable i used to store input and initialises it so some non-zero value. It then continuously enters a while loop so long as that int is non-zero, and in each iteration of the loop it performs a scanf("%d", &i). It then compares that value to 1, and if it's 1 then it'll print the value of a counter that I've set up to track the progress of the Fibonacci computation.

Anyway, all of the above works very smoothly despite my being terribly out of depth with things like threads. However, 开发者_开发问答the problem that I'm having is that when I have to compute, say, the millionth term of the sequence the program takes several minutes to finish, and it would be nice to simply run it in the background. However, if I put the process in the background with ctrl+z and then type bg, the process starts but immediately stops again. I'm guessing that this is because it is constantly requiring input from the user and thus it stops until it gets that input.

Any suggestions for how to circumvent the above issue would be greatly appreciated. I'm not especially worried about this specific issue (computation of the Fibonacci numbers) since that's just a fairly random problem I chose to use for the computation. I'm more interested in the general problem of creating a basic way for the user to input commands to the program, which the program then executes in separate threads, but which still enables the user to run the program in the background if necessary.

Apologies for the rather long-winded question, and thanks in advance for any help!

Phil

Edit: By request, I added (a very simplified version of) the code here. The basic idea is the same: the program launches a lengthy computation in a new thread, then loops for input with scanf. Inputting 0 quits the program, inputting 1 displays a counter indicating the progress of the computation. I'd like to be able to run the program in the background, but since it's continuously asking for input it stops the process immediately. Ignore the arithmetic overflow on the counter; my actual program has data structures to deal with this kind of stuff but I tried to simplify my code as much as possible for readability.

//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);
}


Assuming that you're using POSIX Threads, you can scanf in one thread, let it block until something is entered, and then pthread_cond_signal the other thread to do whatever you want it to do. You can also declare a variable which is updated by the calculating thread and read by the thread with scanf in it. Another more sophisticated way is to listen on a socket for incoming messages, and have a message interpreter part which reads from that socket, and writes back the results. In that case you don't need the scanf, and your program can be running in background.


I would suggest a different approach:

I have a program that processes gigabytes of data and I want to see where I am in the process. This requires a large block of data to be printed, so I only want to do it when asked. I added a signal handler to SIGTSTP (the control-z keystroke sends this signal) which prints out the data. This does not require a separate thread, is simple to implement, and also allows you to signal it if you start the process elsewhere (since you can just send a signal via kill)


The basic problem is that as soon as you try to read from stdin when you are in the background, you will get SIGSTOP, which means it is as if you had hit ctrl-z again immediately. If you need to run in the background, a relatively simlpe change here is to read from a fifo instead of reading from stdin. Use the mkfifo command to create a well knownfifo like so:

mkfifo fib.fifo

The change your main loop like so:


 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
  }


The reads will block until something is written. You have to be careful here if you plan to run multiple instances of this at the same time. Once an item is read from a fifo, it is gone. Only one instance will see the value you have written. You can write to the file as simply as "echo 1 >> fib.fifo"


You can prevent the program from stopping when it tries to read from the terminal while backgrounded by blocking or handling SIGTTIN (and similarly SIGTTOU for writing to the terminal).

While your program is in the background, SIGTTIN and SIGTTOU are delivered when it reads from or writes to the terminal, respectively. By default, your program gets stopped when it receives these signals.

The following bit of code blocks SIGTTIN and SIGTTOU using pthread_sigmask() (since your program uses threads -- otherwise sigprocmask() could be used):

#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 */

The above should be done before you create your threads, since new threads inherit the signal mask. This will allow your program to keep running while writing to and (attempting to) read from the console.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜