2011-02-10 30 views
7

He hecho un programa C bastante simple para calcular los términos de la secuencia de Fibonacci, que estoy ejecutando en Ubuntu. Hice algunas estructuras de datos bastante torpes para que pueda hacer números enteros muy grandes, pero los detalles del programa no son terriblemente importantes, lo que importa es que el programa puede tardar bastante tiempo para realizar los cálculos.Ejecutando un programa C con múltiples hilos en el fondo cuando requiere la entrada del usuario

Por curiosidad, decidí que me gustaría hacer que el programa inicie el cálculo y luego permitir al usuario ingresar un personaje para ver qué tan avanzado está el cálculo. Entonces, en este caso, si el programa está calculando el n-ésimo término de la secuencia de Fibonacci y aún no está hecho, ingresar el número "1" hará que el programa emita el término k que está actualmente computando. Intenté hacer eso usando el siguiente enfoque.

El programa usa scanf para obtener un entero largo que representa el término de la secuencia que debe calcularse. El programa luego crea un hilo con la rutina que escribí para calcular e imprimir el enésimo término de Fibonacci, que sale una vez que ha terminado de hacerlo. Después de eso, el programa crea una variable entera que utilicé para almacenar la entrada y la inicializa para que tenga un valor distinto de cero. Luego ingresa continuamente en un bucle while siempre que int sea distinto de cero, y en cada iteración del bucle realiza un scanf ("% d", & i). Luego compara ese valor a 1, y si es 1, imprimirá el valor de un contador que configuré para seguir el progreso del cálculo de Fibonacci.

De todos modos, todo lo anterior funciona muy bien a pesar de ser terriblemente fuera de profundidad con cosas como hilos. Sin embargo, el problema que estoy teniendo es que cuando tengo que calcular, digamos, el término de la millonésima de la secuencia, el programa tarda varios minutos en terminar, y sería bueno simplemente ejecutarlo en segundo plano. Sin embargo, si pongo el proceso en segundo plano con ctrl + z y luego escribo bg, el proceso comienza pero inmediatamente se detiene nuevamente. Supongo que esto se debe a que constantemente requiere la entrada del usuario y, por lo tanto, se detiene hasta que recibe esa entrada.

Cualquier sugerencia sobre cómo evitar el problema anterior sería muy apreciada. No estoy especialmente preocupado por este tema específico (cálculo de los números de Fibonacci) ya que es solo un problema bastante aleatorio que elegí usar para el cálculo. Estoy más interesado en el problema general de crear una forma básica para que el usuario ingrese comandos en el programa, que luego el programa ejecuta en hilos separados, pero que aún permite al usuario ejecutar el programa en segundo plano si es necesario.

Disculpas por la pregunta bastante larga, y gracias de antemano por cualquier ayuda!

Phil

Editar: Por solicitud, añadí (una versión muy simplificada de) el código aquí. La idea básica es la misma: el programa inicia una computación larga en un nuevo hilo, luego realiza un ciclo de entrada con scanf. Ingresando 0 sale del programa, ingresando 1 muestra un contador que indica el progreso del cálculo. Me gustaría poder ejecutar el programa en segundo plano, pero como está pidiendo continuamente información, detiene el proceso de inmediato. Ignora el desbordamiento aritmético en el contador; mi programa actual tiene estructuras de datos para tratar este tipo de cosas, pero traté de simplificar mi código tanto como sea posible para la legibilidad.

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

En lugar de la larga descripción, puede considerar publicar su código :-) –

+0

¡Punto tomado! Espero que esto aclare un poco las cosas. – ptmx

+2

Sugiero usar un controlador de señal en lugar de entrada con 'scanf' ... Entonces' kill -SIGUSR1 'podría usarse para activar su informe, incluso si la aplicación está en segundo plano. – JimR

Respuesta

1

Suponiendo que está usando hilos POSIX, puede scanf en un hilo, dejar que se bloquean hasta que se introduzca algo, y luego pthread_cond_signal el otro hilo para hacer lo que quiere que haga. También puede declarar una variable que se actualiza mediante el hilo de cálculo y leer por el hilo con scanf en él.Otra forma más sofisticada es escuchar en un socket los mensajes entrantes, y tener una parte del intérprete de mensajes que lee desde ese socket, y escribe los resultados. En ese caso, no necesita el scanf, y su programa se puede ejecutar en segundo plano.

+0

Esto no ayudará con el problema de fondo de OP y el proceso se detuvo en la lectura del terminal. –

+0

Señalar el otro hilo no es el problema; esa parte funciona bien, así que no creo que el uso de señales o el uso de una variable como se describe más arriba resolvería este problema. No estoy familiarizado con los enchufes, pero aprenderé sobre ellos y veré si ese enfoque ayudará aquí. – ptmx

+0

Bueno, si se detiene en el 'scanf' cuando pones el programa en segundo plano es porque un proceso en segundo plano no tiene un' stdin', 'stdouy', y' stderr' válidos.Entonces, dado que ambos subprocesos están técnicamente bajo el mismo espacio de proceso, y la programación de estos subprocesos se realiza con el planificador pthread, cuando todo el proceso se detiene, ambos subprocesos se detienen. Debe cerrar todos los descriptores de archivos estándar antes de poner su programa en segundo plano. – Peyman

1

que sugeriría un enfoque diferente:

Tengo un programa que procesa gigabytes de datos y yo quiero ver dónde estoy en el proceso. Esto requiere la impresión de un gran bloque de datos, por lo que solo quiero hacerlo cuando me lo pidan. Agregué un manejador de señal al SIGTSTP (la combinación de teclas control-z envía esta señal) que imprime los datos. Esto no requiere un hilo separado, es simple de implementar, y también le permite señalarlo si inicia el proceso en otro lugar (ya que puede enviar una señal a través del kill)

1

El problema básico es que tan pronto como usted Intenta leer desde stdin cuando estés en segundo plano, obtendrás SIGSTOP, lo que significa que es como si hubieras pulsado ctrl-z de nuevo inmediatamente. Si necesita ejecutar en segundo plano, un cambio relativamente simple aquí es leer desde una fifo en lugar de leer desde stdin. Utilice el comando mkfifo para crear un pozo knownfifo así:

mkfifo fib.fifo 

El cambio de su bucle principal de este modo:


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 
    } 


El lee bloqueará hasta que algo está escrito. Debe tener cuidado aquí si planea ejecutar varias instancias de esto al mismo tiempo. Una vez que se lee un elemento de un fifo, se va. Solo una instancia verá el valor que ha escrito. Puede escribir en el archivo tan simplemente como "echo 1 >> fib.fifo"

1

Puede evitar que el programa se detenga cuando intenta leer desde el terminal mientras está en segundo plano bloqueando o manejando SIGTTIN (y de manera similar SIGTTOU para escritura a la terminal).

Mientras su programa está en segundo plano, SIGTTIN y SIGTTOU se entregan cuando se lee o escribe en el terminal, respectivamente. Por defecto, su programa se detiene cuando recibe estas señales.

El siguiente segmento de bloques de código SIGTTIN y SIGTTOU usando pthread_sigmask() (ya que el programa utiliza hilos - de lo contrario podrían utilizarse 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 */ 

Lo anterior se debe hacer antes de crear sus hilos, ya que los nuevos los hilos heredan la máscara de señal. Esto permitirá que su programa siga funcionando mientras escribe e (intenta) leer desde la consola.

Cuestiones relacionadas