Anuncios

Bienvenidos sean a este post, hoy veremos como compartir datos entre threads.

Anuncios

Cuando trabajamos con multithreading, una situacion que puede surgirnos es la condicion de carrera. Pero que es una condicion de carrera? Es cuando dos o mas threads estan modificando las mismas variables con mas de un paso, y una sea modificada antes por un thread que no corresponde. Tomemos como ejemplo a cualquier operacion que es ejecutada en un paso simple de una thread segura, a esto se lo denomina como operacion atomica pero cuando esta condicion no esta cumplida dicha operacion se la considera como no atomica.

Anuncios

Como podemos protegernos de esta situacion? Aqui entran en accion los objetos llamados mutexes. Un objeto mutex es el que controla como corre un thread. Cuando un thread bloquea un mutex, el otro thread espera hasta que se termino con el proceso de la informacion y nuevamente desbloquea al mutex. El otro thread bloquea al mutex y comienza a trabajar con la informacion para repetir el proceso sucesivamente. Analicemos el siguiente codigo:

int global = 0;

void inc() {
  global = global + 1;
}
...
std::thread t1{inc};
std::thread t2{inc};
Anuncios

Este codigo simple puede generar una condicion de carrera que comentamos anteriormente. Tomemos este codigo y apliquemos al mutex:

#include <mutex>
...
std::mutex locker;
void inc() {
  locker.lock();
  global = global + 1;
  locker.unlock();
}
...
std::thread t1{inc};
std::thread t2{inc};
Anuncios

Se crea un objeto de tipo mutex y ahora antes de incrementar la variable se bloquea al mutex, se realiza la operacion y una vez realizada se procede a desbloquear al mutex. Esto evitara que cualquier otro thread acceda a la variable. Tambien a partir de C++17 se implemento a un guardian para evitar que olvidemos desbloquearlo:

std::mutex locker;
void inc() {
  std::lock_guard g(locker);
  global = global + 1;
}
Anuncios

Y por lo general, se recomienda mas usar este tipo de mutex. Pero con el uso de mutex surge otro inconveniente, los deadlocks.

Anuncios

Pero que es un deadlock? Es una condicion que se produce cuando dos o mas threads bloquean un mutex y se quedan a la espera del otro para el desbloqueo del mismo. La practica mas comun para evitar un deadlock es que siempre bloquear uno o mas mutexes en el mismo orden. El lenguaje tambien nos provee de la funcion lock para el mismo proposito. Analicemos el siguiente codigo:

void intercambio(X& izq, X& der)
{
  std::lock(izq.mt, der.mt);
  std::lock_guard<std::mutex> lock1(izq.mt, std::adopt_lock);
  std::lock_guard<std::mutex> lock2(der.mt, std::adopt_lock);
}
Anuncios

Esta funcion de intercambio nos servira como ejemplo. Vamos a suponer que la clase X tiene una propiedad llamada mt, la cual es el mutex. Este se encargara de primero bloquear el mutex de izq y luego lo hara con der. Las siguientes dos lineas se encargan de hacer el intercambio.

Anuncios

El mejor consejo para evitar los deadlock es evitar los lock anidados. Es decir, que no debes tomar un lock si ya has adquirido uno. Si este no es el caso, la otra recomendacion es adquirir los bloqueos en un orden fijo, y esto evitara que ocurran los deadlocks.

Anuncios

En resumen, hoy hemos visto como compartir datos entre threads, las distintas formas, un problema que puede suceder, como podemos solucionarlo, el inconveniente que surge con la solucion, y como solucionar al nuevo problema. Espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios

Donation

It’s for maintenance of the site, thanks!

$1.50