UC3M

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2017 - Enero 2018

11.5.2. Procesado concurrente (hilos y mutex) de una tabla

Plan de trabajo

El presente ejercicio tiene por objetivo analizar un programa que realiza el procesado concurrente de una tabla. La idea es repartir la carga entre diferentes hilos que colaboran para guardar el resultado en una variable compartida. Dicha variable está protegida por un cerrojo (mutex).

La idea básica es procesar una estructura de datos larga y repartir el trabajo en varias hebras:

  1. En el ejemplo dado hay 4 hebras, cada una de las cuales suma un cuarto de la tabla y guarda en la variable compartida llamada sum.

  2. Cuando todas las hebras han terminado su ejecución y salen de la función do_work, la función del main acaba.

  3. La hebras comparten una variable global sum que es utilizada por todas las hebras. Las hebras la leen y escriben utilizando un cerrojo para ello. Este cerrojo es necesario para que las hebras sincronicen sus operaciones de lectura y escritura.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/******************************************************************************
* compile with gcc -pthread *.c -o loops
* test with valgrind --tool=helgrind ./lops
*
******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

#define NTHREADS      4
#define ARRAYSIZE   100000000
#define ITERATIONS   ARRAYSIZE / NTHREADS

double  sum=0.0;
double a[ARRAYSIZE];
pthread_mutex_t sum_mutex;


void *do_work(void *tid) 
{
  int i, start, *mytid, end;
  double mysum=0.0;

  /* Initialize my part of the global array and keep local sum */
  mytid = (int *) tid;
  start = (*mytid * ITERATIONS);
  end = start + ITERATIONS;
  printf ("\n[Thread %5d] Doing iterations \t%10d to \t %10d",*mytid,start,end-1); 
  for (i=start; i < end ; i++) {
    a[i] = i * 1.0;
    mysum = mysum + a[i];
    }

  /* Lock the mutex and update the global sum, then exit */
  pthread_mutex_lock (&sum_mutex);
  sum = sum + mysum;
  pthread_mutex_unlock (&sum_mutex);
  pthread_exit(NULL);
}


int main(int argc, char *argv[])
{
  int i, start, tids[NTHREADS];
  pthread_t threads[NTHREADS];
  pthread_attr_t attr;

  /* Pthreads setup: initialize mutex and explicitly create threads in a
     joinable state (for portability).  Pass each thread its loop offset */
  pthread_mutex_init(&sum_mutex, NULL);
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  for (i=0; i<NTHREADS; i++) {
    tids[i] = i;
    pthread_create(&threads[i], &attr, do_work, (void *) &tids[i]);
    }

  /* Wait for all threads to complete then print global sum */ 
  for (i=0; i<NTHREADS; i++) {
    pthread_join(threads[i], NULL);
  }
  printf ("\n[MAIN] Done. Sum= %e", sum);

  sum=0.0;
 /* for (i=0;i<ARRAYSIZE;i++){ 
  a[i] = i*1.0;
  sum = sum + a[i]; }
  printf("\n[MAIN] Check Sum= %e",sum);
*/
  /* Clean up and exit */
  pthread_attr_destroy(&attr);
  pthread_mutex_destroy(&sum_mutex);
  pthread_exit (NULL);
}
 

Vamos a ver de forma práctica la necesidad del cerrojo y a mejorar la calidad del código (eliminando variables globales):

  1. Examine el código, compílelo con el gcc y ejecútelo. Calcule el tiempo de ejecución con 1 hebra, 2 hebras, 4 hebras y 8 hebras. ¿Nota algún tipo de diferencia entre cada uno de estos casos?

  2. Verifique que el código no tiene problemas de concurrencia. Puede usar para ello el siguiente comando valgrind --tool=helgrind ./multi_thread_loop_mutex

  3. Elimine el cerrojo de su código y comprueba como la herramienta Valgrind detecta el problema. Debería de producir una condición de carrera (race condition).

  4. Modifique el ejemplo de partida para que no haya ninguna variable global en el código. Eso hará el código más portable pues éstas hacen que el código sea más complejo.