2012-06-04 15 views
6

el problema está en lo siguiente:El uso de la variable compartida por 10 pthreads

Quiero escribir un pequeño programa que crea 10 hilos y cada impresiones de una banda de rodadura "id" que se pasa el hilo por la función de puntero.

código completo del programa es el siguiente:

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

struct params { 
     pthread_mutex_t mutex; 
     int id; 
}; 

typedef struct params params_t; 

void* hello(void* arg){ 
    int id; 
    pthread_mutex_lock(&(*(params_t*)(arg)).mutex); 
    id = (*(params_t*)(arg)).id; 
    pthread_mutex_unlock(&(*(params_t*)(arg)).mutex); 
    printf("Hello from %d\n", id); 
} 


int main() { 
    pthread_t threads[10]; 
    params_t params; 
    pthread_mutex_init (&params.mutex , NULL); 

    int i; 
    for(i = 0; i < 10; i++) { 
      params.id = i; 
      if(pthread_create(&threads[i], NULL, hello, &params)); 
    } 

    for(i = 0; i < 10; i++) { 
      pthread_join(threads[i], NULL); 
    } 

    return 0; 
} 

La supuesta salida es (no es necesario en este orden):

Hello from 0 
.... 
Hello from 9 

resultado real es:

Hello from 2 
Hello from 3 
Hello from 3 
Hello from 4 
Hello from 5 
Hello from 6 
Hello from 8 
Hello from 9 
Hello from 9 
Hello from 9 

I intentado colocar mutex en diferentes lugares en la función hello(), pero no ayudó.

¿Cómo debo implementar la sincronización de subprocesos?

EDIT: Resultado supuesto no es necesario 0 ... 9 puede ser cualquier combinación de estos números, pero cada uno debería aparecer solo una vez.

Respuesta

2

hay dos problemas:

A. Usted está utilizando un lock pero main no es consciente de este bloqueo.

B. A lock no es suficiente en este caso. Lo que desearía que los hilos cooperaran señalizándose unos a otros (porque quiere main a no incremente la variable hasta que un hilo diga que ha terminado de imprimirla). Puede usar un pthread_cond_t para lograr esto (Look here para obtener más información al respecto). Esto se reduce al siguiente código (básicamente, he añadido un uso apropiado de pthread_cond_t a su código, y un montón de comentarios para explicar lo que está pasando):

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

struct params { 
     pthread_mutex_t mutex; 
     pthread_cond_t done; 
     int id; 
}; 

typedef struct params params_t; 

void* hello(void* arg){ 

    int id; 
    /* Lock. */ 
    pthread_mutex_lock(&(*(params_t*)(arg)).mutex); 

    /* Work. */ 
    id = (*(params_t*)(arg)).id; 
    printf("Hello from %d\n", id); 

    /* Unlock and signal completion. */ 
    pthread_mutex_unlock(&(*(params_t*)(arg)).mutex); 
    pthread_cond_signal (&(*(params_t*)(arg)).done); 

    /* After signalling `main`, the thread could actually 
    go on to do more work in parallel. */ 
} 


int main() { 

    pthread_t threads[10]; 
    params_t params; 
    pthread_mutex_init (&params.mutex , NULL); 
    pthread_cond_init (&params.done, NULL); 

    /* Obtain a lock on the parameter. */ 
    pthread_mutex_lock (&params.mutex); 

    int i; 
    for(i = 0; i < 10; i++) { 

      /* Change the parameter (I own it). */  
      params.id = i; 

      /* Spawn a thread. */ 
      pthread_create(&threads[i], NULL, hello, &params); 

      /* Give up the lock, wait till thread is 'done', 
      then reacquire the lock. */ 
      pthread_cond_wait (&params.done, &params.mutex); 
    } 

    for(i = 0; i < 10; i++) { 
      pthread_join(threads[i], NULL); 
    } 

    /* Destroy all synchronization primitives. */  
    pthread_mutex_destroy (&params.mutex); 
    pthread_cond_destroy (&params.done); 

    return 0; 
} 

veo que el ejemplo que usted está tratando es una programa de juguete para probablemente aprender sobre la biblioteca de hilos POSIX. En el mundo real, como todos sabemos, esto se puede hacer mucho más rápido sin usar hilos. Pero ya lo sabes.

1

El problema es que está modificando el params.id "desprotegido" en main. Esta modificación en main también necesita ser protegida mutex. Puede proteger este acceso localizando esto creando funciones getId() y setId() que bloquearían el mutex y protegerían el acceso al id, de la siguiente manera. Es muy probable que esto dé el problema informado, ya que dependiendo de cuándo el hilo llame a getData() tendrá un valor u otro. Entonces, para resolver esto, puede agregar una función incrementId() y llamarla desde la función hello().

struct params { 
     pthread_mutex_t mutex; 
     int id; 
}; 

typedef struct params params_t; 

int getId(params_t *p) 
{ 
    int id; 
    pthread_mutex_lock(&(p->mutex)); 
    id = p->id; 
    pthread_mutex_unlock(&(p->mutex)); 

    return id; 

} 

void setId(params_t *p, int val) 
{ 
    pthread_mutex_lock(&(p->mutex)); 
    p->id = val; 
    pthread_mutex_unlock(&(p->mutex)); 
} 

void incrementId(params_t *p) 
{ 
    pthread_mutex_lock(&(p->mutex)); 
    p->id++; 
    pthread_mutex_unlock(&(p->mutex)); 
} 

void* hello(void* arg){ 
    params_t *p = (params_t*)(arg); 
    incrementId(p); 
    int id = getId(p); 

    // This could possibly be quite messy since it 
    // could print the data for multiple threads at once 
    printf("Hello from %d\n", id); 
} 


int main() { 
    pthread_t threads[10]; 
    params_t params; 
    params.id = 0; 
    pthread_mutex_init (&params.mutex , NULL); 

    int i; 
    for(i = 0; i < 10; i++) { 
      if(pthread_create(&threads[i], NULL, hello, &params)); 
    } 

    for(i = 0; i < 10; i++) { 
      pthread_join(threads[i], NULL); 
    } 

    return 0; 
} 

Una mejor manera de obtener una ID del tema único sería definir el método hola de la siguiente manera:

void* hello(void* arg){ 
    pthread_t threadId = pthread_self(); 
    printf("Hello from %d\n", threadId); 
} 

Y para evitar el problema con todas las discusiones intentar imprimir a la vez, se puede hacer lo siguiente:

void* hello(void* arg){ 
    params_t *p = (params_t*)(arg); 
    pthread_mutex_lock(&(p->mutex)); 

    p->id++; 
    int id = p->id; 
    printf("Hello from %d\n", id); 

    pthread_mutex_unlock(&(p->mutex)); 
} 
+0

Incluso con el uso sugerido por usted, el OP puede obtener los mismos resultados que ahora. ¿Me estoy perdiendo de algo? – Jay

+0

@Jay, actualicé el código para mostrar mejor el uso. Pero tienes razón, cada hilo no tiene su propio valor, y el valor que tienen depende cuando llaman a getId() – Brady

+0

@Jay, descubrí una forma mejor de hacerlo sin tener que crear una instancia de params por hilo: solo incrementa la identificación desde el hilo. – Brady

7

el problema radica en el código de abajo:

for(i = 0; i < 10; i++) 
{    
    params.id = i;    
if(pthread_create(&threads[i], NULL, hello, &params));  
} 

Su valor de params.id se actualiza en el hilo principal, mientras que usted está pasando el mismo puntero a todos los hilos.

Cree una memoria separada para params asignándola dinámicamente y páselo a diferentes hilos para resolver el problema.

EDIT1: Su uso de mutex para proteger también es una idea incorrecta. Aunque su mutex si se usa en main mientras configura el id también, puede hacer que la actualización sea mutuamente exclusiva, pero es posible que no obtenga el resultado deseado. En lugar de obtener valores de 0 a 9 en diferentes subprocesos, puede obtener todos los 9 o aún varios subprocesos pueden imprimir los mismos valores.

Por lo tanto, utilizar la sincronización de subprocesos no es una buena idea para la salida que está esperando. Si aún necesita usar una variable param entre todos los hilos y obtener una salida de 0 a 9 de cada uno de los hilos, mejor mueva el pthread_join al primer bucle.Esto asegurará que cada subproceso se cree, imprima el valor y luego regrese antes de que el principal genere el siguiente subproceso. En este caso, no necesita el mutex también.

Edit2: En cuanto a la cuestión actualizada, en la que se pidió que no es necesario imprimir los números 0..9 en una secuencia, la impresión puede ser al azar, pero sólo una vez, el problema sigue siendo lo mismo más o menos.

Ahora, digamos, el valor de params.id es primero 0 y se creó el subproceso 0, ahora, el subproceso 0 debe imprimirlo antes de actualizarse en el subproceso principal, de lo contrario, cuando el subproceso 0 accede a él, el valor de params.id se habría convertido en 1 y nunca obtendrás tu conjunto único de valores. Por lo tanto, la forma de garantizar que el hilo 0 impresiones antes de que se actualiza en los principales, Dos maneras para ello:

  • Asegúrese de rosca 0 completa la ejecución y la impresión antes de las actualizaciones principales el valor
  • variables de Uso condición & señalización a asegúrese de que el hilo principal espera para el hilo 0 para completar la impresión antes de que se actualiza el valor (Consulte a la respuesta de Arjun abajo para más detalles)

En mi opinión honesta, que ha seleccionado el problema equivocado para el aprendizaje synchron ization & memoria compartida. Puede probar esto con algunos buenos problemas como "Productor-Consumidor", donde realmente necesita sincronización para que las cosas funcionen.

+0

Podría usar la memoria por separado, pero la idea principal estaba en la variable compartida. Gracias de todos modos. – Alex

+0

@Jay - Esta es una buena respuesta. Sin embargo, hay una manera de hacerlo correctamente sin utilizar un bucle 'join' y sin usar memoria separada. es decir, use un 'pthread_cond_t'. Si bien este es un programa de juguete, la señalización entre subprocesos usando una variable de condición me parece * correcta * para este programa de juguete (y el OP puede aprender algo nuevo). Mira mi respuesta. – ArjunShankar

+0

@ArjunShankar con respecto al programa, es realmente un programa de juguete para comprender cómo funcionan los pthreads. – Alex

0

La manera más fácil de conseguir el resultado deseado sería modificar su función main de la siguiente manera:

int main() { 
    pthread_t threads[10]; 
    params_t params; 
    pthread_mutex_init (&params.mutex , NULL); 

    int i; 
    for(i = 0; i < 10; i++) { 
      params.id = i; 
      if(pthread_create(&threads[i], NULL, hello, &params)); 
      pthread_join(threads[i], NULL); //wait for thread to finish 
    } 

    /*for(i = 0; i < 10; i++) { 
      pthread_join(threads[i], NULL); 
    }*/ 

    return 0; 
} 

salida sería:

Hello from 0 
... 
Hello from 9 

EDIT: Aquí está la sincronización para la cuestión corregido:

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

struct params { 
    pthread_mutex_t* mutex; 
    int id; 
}; 

typedef struct params params_t; 

void* hello(void* arg){ 
    int id = 0; 
    params_t* params = (params_t*)arg; 
    if(params != 0) 
    { 
     id = params->id; 
     delete params; 
     params = 0; 
    } 
    printf("Hello from %d\n", id); 
} 


int main() { 
    pthread_t threads[10]; 
    params_t* params = 0; 
    pthread_mutex_t main_mutex; 
    pthread_mutex_init (&main_mutex , NULL); 

    int i; 
    for(i = 0; i < 10; i++) { 
     params = new params_t(); //create copy of the id to pass to each thread -> each thread will have it's own copy of the id 
     params->id = i; 
     params->mutex = &main_mutex; 
     if(pthread_create(&threads[i], NULL, hello, params)); 
    } 

    for(i = 0; i < 10; i++) { 
     pthread_join(threads[i], NULL); 
    } 

    return 0; 
} 

Cada hilo debe tener su propia copia de la identificación para que los otros hilos no modifiquen la identificación antes de que se imprima.

+0

Esto funcionaría para hilos de corta vida, pero no si el OP quiere que los hilos duren más. Y los nuevos hilos no se crearán hasta que los hilos anteriores se completen. – Brady

+0

Acabo de leer la pregunta. La solución aplica para la declaración actual del problema. No pretende ser genérico. –

+0

@AlexandruC. En este caso, tienes razón, funcionará y es la solución más simple, pero en este caso incluso no necesito una matriz de 'pthread_t'. Solo necesito una variable con este tipo. – Alex

0

sólo estoy poniendo éste aquí para proporcionar otra solución a este problema - éste no implica mutex - sin sincronización - no condicionales, etc. El dfference principal es que estamos utilizando pthread_detach para liberar automáticamente el hilo de recursos al finalizar.

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

#define NUMTHREADS 10   

typedef struct params {  
    int id;  
} params_t;                  

void* hello(void *arg) 
{ 
    params_t *p = (params_t*)arg;  
    int status; 
    status = pthread_detach(pthread_self());  
    if (status !=0)  
    {  
     printf("detaching thread\n");  
     abort();  
    }                   

    printf("Hello from %d\n", p->id); 
    free(p);  
    return NULL; 
}      

int main() 
{ 
    pthread_t thread;  
    params_t *par;  
    int i, status;                
    for (i=0; i<NUMTHREADS; i++)  
    {  
     par = (params_t*)malloc(sizeof(params_t));  
     if (par == NULL)  
     {  
      printf("allocating params_t"); 
      abort();  
     }                  

     par->id = i; 
     status = pthread_create(&thread, NULL, hello, par); 
     if (status != 0)  
      exit(1);  
    }  
    /* DO some more work ...*/              
    sleep(3);                   
    exit(0); 
} 
Cuestiones relacionadas