Foros del Web » Programación para mayores de 30 ;) » C/C++ »

[SOLUCIONADO] Uso de Threads en C

Estas en el tema de Uso de Threads en C en el foro de C/C++ en Foros del Web. Hola, soy nuevo en el foro, tengo un problema con los valores que regresan los threads. El programa debe crear 5 hilos de ejcución. Cada ...
  #1 (permalink)  
Antiguo 08/03/2015, 16:32
 
Fecha de Ingreso: marzo-2015
Mensajes: 9
Antigüedad: 9 años, 9 meses
Puntos: 0
Pregunta Uso de Threads en C

Hola, soy nuevo en el foro, tengo un problema con los valores que regresan los threads.
El programa debe crear 5 hilos de ejcución. Cada hilo determina un número aleatorio entre 1 y 10. El hilo principal espera a que los hilos terminen, calcula la suma de los números generados aleatoriamente por cada hilo y muestra en pantalla dicha suma.

Me regresa datos que no corresponden (ejemplo):
Número aleatorio: 8
Número aleatorio: 7
Número aleatorio: 7
Número aleatorio: 7
Número aleatorio: 7
Suma de los números: 7

Aquí el código:
Código C:
Ver original
  1. #include <pthread.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4.  
  5. struct hilos_random_parms
  6. {
  7.     int randi;
  8. };
  9.  
  10. int aleatorios[5];
  11. int cont = 0;
  12.  
  13. void* hilos_random(void* parameters)
  14. {
  15.     struct hilos_random_parms* p = (struct hilos_random_parms*)parameters;
  16.     srand(time(NULL));
  17.     aleatorios[cont] = (rand() % p->randi) + 1;    
  18.     printf("Número aleatorio: %d \n", aleatorios[cont]);
  19.     cont++;
  20.     return NULL;
  21. }
  22.  
  23. int main()
  24. {
  25.     pthread_t thread1_id;
  26.     pthread_t thread2_id;
  27.     pthread_t thread3_id;
  28.     pthread_t thread4_id;
  29.     pthread_t thread5_id;
  30.    
  31.     struct hilos_random_parms thread1_args;
  32.  
  33.     thread1_args.randi = 10;   
  34.  
  35.     pthread_create(&thread1_id, NULL, &hilos_random, &thread1_args);
  36.     pthread_create(&thread2_id, NULL, &hilos_random, &thread1_args);
  37.     pthread_create(&thread3_id, NULL, &hilos_random, &thread1_args);
  38.     pthread_create(&thread4_id, NULL, &hilos_random, &thread1_args);
  39.     pthread_create(&thread5_id, NULL, &hilos_random, &thread1_args);
  40.    
  41.     pthread_join(thread1_id, NULL);
  42.     pthread_join(thread2_id, NULL);
  43.     pthread_join(thread3_id, NULL);
  44.     pthread_join(thread4_id, NULL);
  45.     pthread_join(thread5_id, NULL);
  46.    
  47.     int total = 0;
  48.     int i;
  49.     for (i = 0; i < 5; i++)
  50.     {
  51.         total += aleatorios[i];
  52.     }
  53.    
  54.     printf("Suma de los números: %d \n", total);
  55.  
  56.     return 0;
  57. }
  #2 (permalink)  
Antiguo 08/03/2015, 17:37
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 9 meses
Puntos: 1360
Respuesta: Uso de Threads en C

Bueno tienes problemas de concurrencia, porque usas un contador en lugar de enviar un parámetro con el indice que usara.

Supón que inicias todos los threads y se empieza a ejecutar al mismo tiempo. Obviamente cont va a tener 0 en varias de esas llamadas de hecho lo puedes visualizar mejor si imprimes cont.

Código C:
Ver original
  1. printf("Número aleatorio: (%d) %d \n", cont, aleatorios[cont]);

Para evitar eso, creas un arreglo de parametros y lo llenas con los parametros que ocupas.
Código C:
Ver original
  1. struct hilos_random_parms
  2. {
  3.     int randi;
  4.     int index;
  5. };
  6.  
  7. struct hilos_random_parms thread1_args[5];
  8. int i;
  9. for(i = 0; i < 5; i++) {
  10.     thread1_args[i].randi = 10;
  11.     thread1_args[i].index = i;
  12. }

Haces lo mismo al pasarlos
Código C:
Ver original
  1. pthread_create(&thread1_id, NULL, &hilos_random, &thread1_args[0]);
  2.     pthread_create(&thread2_id, NULL, &hilos_random, &thread1_args[1]);
  3.     pthread_create(&thread3_id, NULL, &hilos_random, &thread1_args[2]);
  4.     pthread_create(&thread4_id, NULL, &hilos_random, &thread1_args[3]);
  5.     pthread_create(&thread5_id, NULL, &hilos_random, &thread1_args[4]);

Por ultimo sustituyes cont
Código C:
Ver original
  1. int cont = p->index;
  2.  
  3. // Lo añades al SEED porque si no obtendras siempre los mismos numeros.
  4. srand(time(NULL) + cont);


Al final te quedara algo así:
Código C:
Ver original
  1. #include <pthread.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4.  
  5. struct hilos_random_parms
  6. {
  7.     int randi;
  8.     int index;
  9. };
  10.  
  11. int aleatorios[5];
  12.  
  13. void* hilos_random(void* parameters)
  14. {
  15.     struct hilos_random_parms* p = (struct hilos_random_parms*)parameters;
  16.     int cont = p->index;
  17.     srand(time(NULL) + cont);
  18.     aleatorios[cont] = (rand() % p->randi) + 1;
  19.     printf("Número aleatorio: (%d) %d \n", cont, aleatorios[cont]);
  20.     cont++;
  21.     return NULL;
  22. }
  23.  
  24. int main()
  25. {
  26.     pthread_t thread1_id;
  27.     pthread_t thread2_id;
  28.     pthread_t thread3_id;
  29.     pthread_t thread4_id;
  30.     pthread_t thread5_id;
  31.  
  32.     struct hilos_random_parms thread1_args[5];
  33.     int i;
  34.     for(i = 0; i < 5; i++) {
  35.         thread1_args[i].randi = 10;
  36.         thread1_args[i].index = i;
  37.     }
  38.  
  39.  
  40.     pthread_create(&thread1_id, NULL, &hilos_random, &thread1_args[0]);
  41.     pthread_create(&thread2_id, NULL, &hilos_random, &thread1_args[1]);
  42.     pthread_create(&thread3_id, NULL, &hilos_random, &thread1_args[2]);
  43.     pthread_create(&thread4_id, NULL, &hilos_random, &thread1_args[3]);
  44.     pthread_create(&thread5_id, NULL, &hilos_random, &thread1_args[4]);
  45.  
  46.     pthread_join(thread1_id, NULL);
  47.     pthread_join(thread2_id, NULL);
  48.     pthread_join(thread3_id, NULL);
  49.     pthread_join(thread4_id, NULL);
  50.     pthread_join(thread5_id, NULL);
  51.  
  52.     int total = 0;
  53.     for (i = 0; i < 5; i++)
  54.     {
  55.         total += aleatorios[i];
  56.     }
  57.  
  58.     printf("Suma de los números: %d \n", total);
  59.  
  60.     return 0;
  61. }

Pero es bastante sencillo:
Lecturas concurrentes si puedes.
Escrituras concurrentes solo uno a la vez.

Existen escenarios peores como deathlocks, donde te quedas esperando a que X thread termine pero ese X thread esta esperando a que tu termines.

También escenarios donde dices: Quiero que X se ejecute antes de Y.

Te recomiendo The little book about semaphores
  #3 (permalink)  
Antiguo 08/03/2015, 18:45
 
Fecha de Ingreso: marzo-2015
Mensajes: 9
Antigüedad: 9 años, 9 meses
Puntos: 0
Respuesta: Uso de Threads en C

Muchas gracias, ha funcionado.
Aquí no entendí porqué se le suma el cont, si me pudieras explicar.
Código C:
Ver original
  1. srand(time(NULL) + cont);

Sólo esta linea quedó volando.
Código C:
Ver original
  1. cont++;
  #4 (permalink)  
Antiguo 08/03/2015, 22:09
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 9 meses
Puntos: 1360
Respuesta: Uso de Threads en C

Tip puedes usar srand(time(NULL)); en lugar de srand(time(NULL) + cont); y ver que pasa.

Si, cont++ quedo volando, si hubiera quitado la declaración de esa variable hubiera quitado todas las lineas relacionadas.
  #5 (permalink)  
Antiguo 09/03/2015, 03:08
Avatar de jc_moj  
Fecha de Ingreso: septiembre-2009
Ubicación: Andalucía
Mensajes: 137
Antigüedad: 15 años, 3 meses
Puntos: 12
Respuesta: Uso de Threads en C

Hola

¿No sería más correcto sacar el srand de la función y ponerla al inicio del main?

Así no habría que andar sumándole el cont, se siembra la semilla al iniciar y ya está.

Saludos
  #6 (permalink)  
Antiguo 09/03/2015, 15:10
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 9 meses
Puntos: 1360
Respuesta: Uso de Threads en C

Cita:
Iniciado por jc_moj Ver Mensaje
¿No sería más correcto sacar el srand de la función y ponerla al inicio del main?
Así no habría que andar sumándole el cont, se siembra la semilla al iniciar y ya está.
Otra vez te toparías con problemas con concurrencia al alterar el estado de srand al mismo tiempo. Esto es porque srand no es thread safe.

Por eso tienes que tener conocimiento sobre las funciones que usaras en los threads, por que puede resultar en algo que no deseas.
  #7 (permalink)  
Antiguo 09/03/2015, 15:59
Avatar de jc_moj  
Fecha de Ingreso: septiembre-2009
Ubicación: Andalucía
Mensajes: 137
Antigüedad: 15 años, 3 meses
Puntos: 12
Respuesta: Uso de Threads en C

Hola

Bueno, no estoy muy puesto en este tema, por eso me intriga y me gusta me aclaréis.

Cita:
Iniciado por razpeitia Ver Mensaje
Otra vez te toparías con problemas con concurrencia al alterar el estado de srand al mismo tiempo. Esto es porque srand no es thread safe.

Por eso tienes que tener conocimiento sobre las funciones que usaras en los threads, por que puede resultar en algo que no deseas.
He modificado el código que pusiste arriba, poniendo el srand como primera línea del main, justo antes de crear los thread y tras ejecutar el programa varias veces, obtengo unos números aleatorios muy similares a dejando el srand en la función.

¿Al usar srand antes de crear los thread no queda ya la semilla establecida y por tanto, cada thread, lo pida cuando lo pida, obtiene ya un número aleatorio adecuado?

Saludos
  #8 (permalink)  
Antiguo 09/03/2015, 18:04
Avatar de razpeitia
Moderador
 
Fecha de Ingreso: marzo-2005
Ubicación: Monterrey, México
Mensajes: 7.321
Antigüedad: 19 años, 9 meses
Puntos: 1360
Respuesta: Uso de Threads en C

Cita:
Iniciado por jc_moj Ver Mensaje
¿Al usar srand antes de crear los thread no queda ya la semilla establecida y por tanto, cada thread, lo pida cuando lo pida, obtiene ya un número aleatorio adecuado?
Si, cada thread cuando lo pida ya te generara un numero random.

Excepto que como lo estas pidiendo números aletorios al mismo tiempo no es sorpresa que te regrese el mismo numero en diferentes threads.

Esto es por varias cosas:
1. srand tiene un estado interno
2. mandas a llamar srand al mismo tiempo y por lo tanto en el mismo estado interno
3. Obtienes el mismo numero aleatorio en varios threads

Este mismo ejemplo lo puedes con una simple suma.
Imagina que tienes una variable
Código:
i = 0
Y que al mismo tiempo sumas en 2 threads
Código:
i++
¿Cual crees que sea el resultado?
Bueno el resultado puede ser 1 o 2.

¿Porque pasa esto?
Por que como se ejecuta al mismo tiempo los 2 threads tienen el mismo estado (0).
Puede ser que en algunos casos, toda la suma del primer thread antes del segundo. Lo cual parecerá que funciona bien.

Ejemplo sencillo

Código C:
Ver original
  1. #include <pthread.h>
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4.  
  5. #define N 5
  6.  
  7. int count = 0;
  8.  
  9. void* work(void* parameters)
  10. {
  11.     printf("Count: %d \n", count);
  12.     count += 1;
  13.     return NULL;
  14. }
  15.  
  16. int main()
  17. {
  18.     pthread_t thread[N];
  19.     int i;
  20.     for(i = 0; i < N; i++)
  21.         pthread_create(&thread[i], NULL, work, NULL);
  22.     for(i = 0; i < N; i++)
  23.         pthread_join(thread[i], NULL);
  24.  
  25.     return 0;
  26. }

Etiquetas: linux, rand, threads
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 09:04.