Concurrencia en C++ moderno
Concurrencia en C++ modernocodemotion 2014
J. Daniel Garcia
Grupo ARCOSUniversidad Carlos III de Madrid
21 de noviembre de 2014
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 1/52
Concurrencia en C++ moderno
Aviso
c Esta obra está bajo una Licencia Creative CommonsAtribución-NoComercial-SinDerivar 4.0 Internacional.
b Debes dar crédito en la obra en la forma especificadapor el autor o licenciante.
e El licenciante permite copiar, distribuir y comunicar pú-blicamente la obra. A cambio, esta obra no puede serutilizada con fines comerciales — a menos que se ob-tenga el permiso expreso del licenciante.
d El licenciante permite copiar, distribuir, transmitir y co-municar públicamente solamente copias inalteradas dela obra – no obras derivadas basadas en ella.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 2/52
Concurrencia en C++ moderno
ARCOS@uc3m
UC3M: Una universidad joven, internacional y orientada ala investigación.ARCOS: Un grupo de investigación aplicada.
Líneas: Computación de altas prestaciones, Big data,Sistemas Ciberfísicos, y Modelos de programación para lamejora de las aplicaciones
Mejorando las aplicaciones:REPARA: Reengineering and Enabling Performance andpoweR of Applications.RePhrase: REfactoring Parallel Heterogeneous ResourceAware Applications.
Normalización:ISO/IEC JTC/SC22/WG21.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 3/52
Concurrencia en C++ moderno
La concurrencia está aquí
1 La concurrencia está aquí
2 Concurrencia mínima
3 Hilos
4 Exclusión mutua
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 4/52
Concurrencia en C++ moderno
La concurrencia está aquí
The free lunch is over
1 La concurrencia está aquíThe free lunch is overC++ y la concurrencia
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 5/52
Concurrencia en C++ moderno
La concurrencia está aquí
The free lunch is over
Evolución
Hasta 2005 (aprox.) incremento sostenido en frecuenciasde reloj.
Incrementos del rendmiento en torno al 52 % anual.
Pero entonces . . .Se acabó la fiesta (The free lunch is over).Estabilización de frecuencias de reloj en torno a 3GHz.La industria apuesta por multi/many-core.
La concurrencia y el paralelismo se convierten en másrelevantes.
Recuerda: No son lo mismo pero muy interrelacionados.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 6/52
Concurrencia en C++ moderno
La concurrencia está aquí
The free lunch is over
Evolución
Hasta 2005 (aprox.) incremento sostenido en frecuenciasde reloj.
Incrementos del rendmiento en torno al 52 % anual.
Pero entonces . . .Se acabó la fiesta (The free lunch is over).Estabilización de frecuencias de reloj en torno a 3GHz.La industria apuesta por multi/many-core.
La concurrencia y el paralelismo se convierten en másrelevantes.
Recuerda: No son lo mismo pero muy interrelacionados.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 6/52
Concurrencia en C++ moderno
La concurrencia está aquí
The free lunch is over
Evolución
Hasta 2005 (aprox.) incremento sostenido en frecuenciasde reloj.
Incrementos del rendmiento en torno al 52 % anual.
Pero entonces . . .Se acabó la fiesta (The free lunch is over).Estabilización de frecuencias de reloj en torno a 3GHz.La industria apuesta por multi/many-core.
La concurrencia y el paralelismo se convierten en másrelevantes.
Recuerda: No son lo mismo pero muy interrelacionados.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 6/52
Concurrencia en C++ moderno
La concurrencia está aquí
The free lunch is over
Concurrencia y paralelismo
Concurrencia: Ejecución de varias tareas de forma que elinicio de una se encuentra entre el principio y final de otra.
Puede darse en un único procesador.Objetivos:
Mejorar aprovechamiento de recursos (trabajos por unidadde tiempo).Mejorar experiencia de respuesta en presencia de E/S.Mejorar estructura de programas basados en tareas.
Paralelismo: Ejecución de varias tareas de formasimultánea.
Típicamente cada tarea usa un procesador (o un core).Objetivos:
Mejorar el rendimiento (tiempo de un trabajo).Mejorar la escalabilidad.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 7/52
Concurrencia en C++ moderno
La concurrencia está aquí
The free lunch is over
Concurrencia y paralelismo
Concurrencia: Ejecución de varias tareas de forma que elinicio de una se encuentra entre el principio y final de otra.
Puede darse en un único procesador.Objetivos:
Mejorar aprovechamiento de recursos (trabajos por unidadde tiempo).Mejorar experiencia de respuesta en presencia de E/S.Mejorar estructura de programas basados en tareas.
Paralelismo: Ejecución de varias tareas de formasimultánea.
Típicamente cada tarea usa un procesador (o un core).Objetivos:
Mejorar el rendimiento (tiempo de un trabajo).Mejorar la escalabilidad.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 7/52
Concurrencia en C++ moderno
La concurrencia está aquí
C++ y la concurrencia
1 La concurrencia está aquíThe free lunch is overC++ y la concurrencia
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 8/52
Concurrencia en C++ moderno
La concurrencia está aquí
C++ y la concurrencia
¿Por qué?
Portabilidad:Antes de 2011 diversos modelos de programaciónconcurrente.
Windows Threads, POSIX Threads, . . .Extensiones en el compilador: __declspec( thread ),__thread, . . .
Necesidad de código no portable para operaciones libresde cerrojos.
Típicamente en ensamblador.
Completitud y corrección:Un modelo de hilos no puede implementarse sin soportedel compilador.
H. Bohem. Threads cannot be implemented as a library.PLDI’2005.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 9/52
Concurrencia en C++ moderno
La concurrencia está aquí
C++ y la concurrencia
¿Por qué?
Portabilidad:Antes de 2011 diversos modelos de programaciónconcurrente.
Windows Threads, POSIX Threads, . . .Extensiones en el compilador: __declspec( thread ),__thread, . . .
Necesidad de código no portable para operaciones libresde cerrojos.
Típicamente en ensamblador.
Completitud y corrección:Un modelo de hilos no puede implementarse sin soportedel compilador.
H. Bohem. Threads cannot be implemented as a library.PLDI’2005.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 9/52
Concurrencia en C++ moderno
La concurrencia está aquí
C++ y la concurrencia
¿Por qué?
Portabilidad:Antes de 2011 diversos modelos de programaciónconcurrente.
Windows Threads, POSIX Threads, . . .Extensiones en el compilador: __declspec( thread ),__thread, . . .
Necesidad de código no portable para operaciones libresde cerrojos.
Típicamente en ensamblador.
Completitud y corrección:Un modelo de hilos no puede implementarse sin soportedel compilador.
H. Bohem. Threads cannot be implemented as a library.PLDI’2005.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 9/52
Concurrencia en C++ moderno
La concurrencia está aquí
C++ y la concurrencia
Y además . . .
Formalización del modelo de memoria.Hasta 2005 varios fabricantes no habían clarificado sumodelo de consistencia de memoria.Formalización compleja.Problemas de implementación en lenguajes (Java, C, C++).Efecto: Clarificación de los fabricantes de procesadores.
Efecto sobre C11.Incluisión de un modelo de hilos muy similar.Portabilidad de hilos en C.
Mejor que PThreads!
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 10/52
Concurrencia en C++ moderno
La concurrencia está aquí
C++ y la concurrencia
Y además . . .
Formalización del modelo de memoria.Hasta 2005 varios fabricantes no habían clarificado sumodelo de consistencia de memoria.Formalización compleja.Problemas de implementación en lenguajes (Java, C, C++).Efecto: Clarificación de los fabricantes de procesadores.
Efecto sobre C11.Incluisión de un modelo de hilos muy similar.Portabilidad de hilos en C.
Mejor que PThreads!
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 10/52
Concurrencia en C++ moderno
Concurrencia mínima
1 La concurrencia está aquí
2 Concurrencia mínima
3 Hilos
4 Exclusión mutua
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 11/52
Concurrencia en C++ moderno
Concurrencia mínima
Tareas asíncronas
2 Concurrencia mínimaTareas asíncronasParámetros y resultados de una tarea
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 12/52
Concurrencia en C++ moderno
Concurrencia mínima
Tareas asíncronas
Historia de dos tareas
tasks.h
void f () {using namespace std;constexpr int max = 10;for ( int i=0; i<max; ++i) {
this_thread :: sleep_for(chrono::milliseconds(5)) ;cout.put( ’+’ ) . flush () ;
}}
void g() {using namespace std;constexpr int max = 20;for ( int i=0; i<max; ++i) {
this_thread :: sleep_for(chrono::milliseconds(5)) ;cout.put( ’∗ ’ ) . flush () ;
}}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 13/52
Concurrencia en C++ moderno
Concurrencia mínima
Tareas asíncronas
Ejecutando las tareas secuencialmente
main1.cpp
#include <chrono>#include <thread>#include <iostream>#include "tasks.h"
int main() {f () ;g() ;return 0;
}
$ time ./test1++++++++++********************real 0m0.158suser 0m0.004ssys 0m0.000s
Ambas tareas se pueden ejecutar concurrentemente.cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 14/52
Concurrencia en C++ moderno
Concurrencia mínima
Tareas asíncronas
Ejecutando las tareas concurrentemente
main2.cpp
#include <chrono>#include <thread>#include <future>#include <iostream>#include "tasks.h"
int main() {using namespace std;
auto r1 = async(launch::async, f) ;auto r2 = async(launch::async, g);
r1.get() ;r2.get() ;
return 0;}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 15/52
Concurrencia en C++ moderno
Concurrencia mínima
Tareas asíncronas
Concurrencia
$ time ./test1+*+**+*+*+*+*+*+*+*+**********real 0m0.106suser 0m0.004ssys 0m0.000s
Salida entrelazada a cout.Aprovechamiento del tiempo.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 16/52
Concurrencia en C++ moderno
Concurrencia mínima
Tareas asíncronas
Políticas de ejecución
La biblioteca ofrece dos políticas de ejecución.std::launch::async. Ejecución asíncrona posiblemente(as-if) en otro hilo.std::launch::deferred. Ejecución en el mismo hilo cuandose invoca a get.
¿Y si no se especifica la política?
std :: async(f) ;
Como si se invocase a std::launch::async |std::launch::deferred.La implementación decide.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 17/52
Concurrencia en C++ moderno
Concurrencia mínima
Tareas asíncronas
Políticas de ejecución
La biblioteca ofrece dos políticas de ejecución.std::launch::async. Ejecución asíncrona posiblemente(as-if) en otro hilo.std::launch::deferred. Ejecución en el mismo hilo cuandose invoca a get.
¿Y si no se especifica la política?
std :: async(f) ;
Como si se invocase a std::launch::async |std::launch::deferred.La implementación decide.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 17/52
Concurrencia en C++ moderno
Concurrencia mínima
Parámetros y resultados de una tarea
2 Concurrencia mínimaTareas asíncronasParámetros y resultados de una tarea
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 18/52
Concurrencia en C++ moderno
Concurrencia mínima
Parámetros y resultados de una tarea
Una tarea de cálculo: main3.cpp (I)
#include <chrono>#include <thread>#include <future>#include <random>#include <iostream>
double f(int s, int n) {using namespace std;
default_random_engine engine(s);normal_distribution<double> gen(1.0, 0.25);
double r = 0.0;for ( int i=0; i<n; ++i) {
r += gen(engine);}
return r /n;}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 19/52
Concurrencia en C++ moderno
Concurrencia mínima
Parámetros y resultados de una tarea
Pasando parámetros y recogiendo resultados
main3.cpp (y II)
int main() {using namespace std;
auto r1 = async(launch::async,f, 3, 1000000);auto r2 = async(launch::async,f, 115, 2500000);
cout << r1.get() << endl;cout << r2.get() << endl;
return 0;}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 20/52
Concurrencia en C++ moderno
Concurrencia mínima
Parámetros y resultados de una tarea
Una tarea excepcional
main4.cpp (I)
#include <chrono>#include <thread>#include <future>#include <random>#include <iostream>
class mi_error {};
double f(int n) {if (n==0) throw mi_error{};else return 1000.0/n;
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 21/52
Concurrencia en C++ moderno
Concurrencia mínima
Parámetros y resultados de una tarea
Una tarea excepcional
main4.cpp (y III)
int main() {using namespace std;
auto r1 = async(launch::async,f, 0);
try {cout << r1.get() << endl;
}catch (const mi_error &) {
cerr << "Error" << endl;}
return 0;}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 22/52
Concurrencia en C++ moderno
Hilos
1 La concurrencia está aquí
2 Concurrencia mínima
3 Hilos
4 Exclusión mutua
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 23/52
Concurrencia en C++ moderno
Hilos
Introducción
3 HilosIntroducciónConstrucción e identidadTerminación de hilos
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 24/52
Concurrencia en C++ moderno
Hilos
Introducción
La clase thread
La abstracción de hilo representada mediante clasestd::thread.Todos los hilos de una aplicación se ejecutan en el mismoespacio de direcciones.Cada hilo tiene su propia pila.
thread representa un enlace a un hilo del sistema.No se pueden copiar.Si se pueden mover.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 25/52
Concurrencia en C++ moderno
Hilos
Introducción
La clase thread
La abstracción de hilo representada mediante clasestd::thread.Todos los hilos de una aplicación se ejecutan en el mismoespacio de direcciones.Cada hilo tiene su propia pila.thread representa un enlace a un hilo del sistema.
No se pueden copiar.Si se pueden mover.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 25/52
Concurrencia en C++ moderno
Hilos
Introducción
Lanzamiento de hilos
Un hilo representado por la clase std::thread.Normalmente representa un hilo del SO.
void f1 () ;
struct f2 {void operator()();
};
void g() {thread t1{f1 }; // Lanza un hilo que ejecuta f1 ()thread t2{f2 () }; // Lanza un hilo que ejecuta f2 :: operator() ()
t1 . join () ; // Espera a que t1 termine.t2 . join () ; // Espera a que t2 termine.
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 26/52
Concurrencia en C++ moderno
Hilos
Introducción
Objetos compartidos
Dos hilos pueden acceder a un objeto compartido.Posibilidad de carreras de datos.
Acceso a datos
#include <iostream>#include <thread>
int x = 42;
void f () { ++x; }void g() { x=0; }
Ejecución de hilos
void carrera() {using namespace std;thread t1{ f };thread t2{g};
t1 . join () ;t2 . join () ;
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 27/52
Concurrencia en C++ moderno
Hilos
Introducción
Un poco de ayuda
$ valgrind --tool=helgrind ./carrera==21180== Helgrind, a thread error detector==21180== Copyright (C) 2007-2011, and GNU GPL’d, by OpenWorks
LLP et al.[...]==21180== Possible data race during read of size 8 at 0x5C27060
by thread #1[...]==21180== This conflicts with a previous write of size 8 by
thread #3[...]==21180== Possible data race during write of size 8 at 0x5C27050
by thread #1[...]==21180== This conflicts with a previous read of size 8 by
thread #3[...]
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 28/52
Concurrencia en C++ moderno
Hilos
Introducción
Paso de argumentos
params.cpp
#include <thread>
void f1( int x) ;
struct f2 {void operator()();f2( int px) : x{px} {}int x;
};
params.cpp
void g() {using namespace std;thread t1{f1 , 10}; // Ejecuta f1(10)thread t2{f2 {20}}; // Ejecuta f2 {20}.operator() ()thread t3 {[] { f1(42); } }; // Ejecuta una lambda
t1 . join () ;t2 . join () ;t3 . join () ;
}
Paso de parámetros simplificado sin necesidad de casts.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 29/52
Concurrencia en C++ moderno
Hilos
Introducción
Paso de argumentos
params.cpp
#include <thread>
void f1( int x) ;
struct f2 {void operator()();f2( int px) : x{px} {}int x;
};
params.cpp
void g() {using namespace std;thread t1{f1 , 10}; // Ejecuta f1(10)thread t2{f2 {20}}; // Ejecuta f2 {20}.operator() ()thread t3 {[] { f1(42); } }; // Ejecuta una lambda
t1 . join () ;t2 . join () ;t3 . join () ;
}
Paso de parámetros simplificado sin necesidad de casts.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 29/52
Concurrencia en C++ moderno
Hilos
Construcción e identidad
3 HilosIntroducciónConstrucción e identidadTerminación de hilos
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 30/52
Concurrencia en C++ moderno
Hilos
Construcción e identidad
Construcción de hilos
Un hilo se construye con una función y los argumentosque se debe pasar a la función.
Plantilla con número variable de argumentos.Seguro en tipos.
Ejemplo
void f () ;void g(int , double);
thread t1{ f }; // OKthread t2{ f , 1}; // Error: demasiados argumentos.
thread t3{g, 1, 0.5}; // OKthread t4{g}; // Error: faltan argumentos.thread t5{g, 1, "Hola"}; // Error: tipos incorrectos
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 31/52
Concurrencia en C++ moderno
Hilos
Construcción e identidad
Construcción y referencias
El constructor de thread es una plantilla con argumentosvariables.template <class F, class ...Args>explicit thread(F&& f, Args&&... args);
El paso de parámetros a un hilo es por valor.Para forzar el paso de parámetros por referencia:
Usar una función de ayuda para reference_wrapper.Usar lambdas y capturas por referencia.
void f ( registro & r) ;
void g(registro & s) {thread t1{ f ,s };thread t2{ f , ref (s) };thread t3 {[&] { f (s) ; }};
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 32/52
Concurrencia en C++ moderno
Hilos
Construcción e identidad
Construcción y referencias
El constructor de thread es una plantilla con argumentosvariables.template <class F, class ...Args>explicit thread(F&& f, Args&&... args);
El paso de parámetros a un hilo es por valor.Para forzar el paso de parámetros por referencia:
Usar una función de ayuda para reference_wrapper.Usar lambdas y capturas por referencia.
void f ( registro & r) ;
void g(registro & s) {thread t1{ f ,s };thread t2{ f , ref (s) };thread t3 {[&] { f (s) ; }};
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 32/52
Concurrencia en C++ moderno
Hilos
Construcción e identidad
Construcción en dos etapas
La construcción incluye el inicio de la ejecución del hilo.No hay operación separada para iniciar la ejecución.
Productor/Consumidor
struct productor {productor(cola<peticiones> & c);void operator()();// ...
};
struct consumidor {consumidor(cola<peticiones> & c);void operator()();// ...
};
Etapas de construcción
cola<peticiones> c;productor prod{c};consumidor cons{c};
thread tp{prod};thread tc {cons};
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 33/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
3 HilosIntroducciónConstrucción e identidadTerminación de hilos
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 34/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
Unión de tareas
Cuando un hilo desea esperar a que otro hilo finalicepuede usar la operación join().
t.join() → espera a que t haya finalizado.
hilos/join1.cpp
void f ( int i ) ;
void g() {std :: vector<std :: thread> vt;for ( int i=0; i< 8; ++i) {
vt .push_back(std::thread(f, i ) ) ;}
for (auto & t : vt ) { // Espera a que todos los hilos terminent . join () ;
}}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 35/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
Ejemplo
progreso.cpp
void actualiza_barra() {while (! tarea_terminada()) {
this_thread :: sleep_for(chrono::second(1))actualiza_progreso();
}}
void f () {thread t {actualiza_barra};alguna_otra_cosa();t . join () ;
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 36/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
¿Qué pasa si se olvida join?
Si se sale del alcance donde se define el hilo, se invoca aldestructor.Problema: Se podría perder el vínculo con un hilo delsistema que se seguiría ejecutando y al que no se podríaacceder.
Si no se ha hecho join() el destructor llama a terminate().void actualiza () {
for (;;) {muestra_reloj(stead_clock::now());this_thread :: sleep_for(second{1});
}}
void f () {thread t (actualiza) ;
}
Se ejecuta terminate() al abandonar f().cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 37/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
Problemas con la destrucción
void ejemplo() {thread t1{ f }; // Hilo que ejecuta la tarea fthread t2 ; // Hilo vacío.
if (modo == modo1) {thread tg {g};// ...t2 = move(tg); // tg vacía, t2 asociada a g()
}
vector<int> v{10000}; // Podría lanzar excepcionest1 . join () ;t2 . join () ;
}
¿Qué ocurre si el constructor de v lanza una excepción.¿Qué ocurre si se llega al final con modo==modo1?
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 38/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
Hilo automático
Patrón RAII
struct hilo_automatico : thread {using thread::thread;~hilo_automatico() {
if ( t . joinable () ) t . join () ;}
};
Simplificación de código
void ejemplo() {hilo_automatico t1{ f }; // Hilo f ()hilo_automatico t2; // Hilo vacío.
if (modo == modo1) {hilo_automatico tg {g};// ...// tg vacía, t2 asociada a g()t2 = move(tg);
}
// Podría lanzar excepcionesvector<int> v{10000};
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 39/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
Hilo automático
Patrón RAII
struct hilo_automatico : thread {using thread::thread;~hilo_automatico() {
if ( t . joinable () ) t . join () ;}
};
Simplificación de código
void ejemplo() {hilo_automatico t1{ f }; // Hilo f ()hilo_automatico t2; // Hilo vacío.
if (modo == modo1) {hilo_automatico tg {g};// ...// tg vacía, t2 asociada a g()t2 = move(tg);
}
// Podría lanzar excepcionesvector<int> v{10000};
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 39/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
Hilos no asociados
Se puede usar detach() para indicar que un hilo sigaejecutando después de que el destructor se ejecute.Útil para tareas que se ejecutan como demonios.
Un demonio
void actualiza () {for (;;) {
muestra_reloj(stead_clock::now());this_thread :: sleep_for(second{1});
}}
void f () {thread t (actualiza) ;t .detach();
}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 40/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
Problemas con hilos no asociados
Incovenientes:Se pierde el control de qué hilos están activos.No se sabe si se puede usar el resultado generado por unhilo.No se sabe si un hilo ha liberado sus recursos.Se podría acabar accediendo a objetos que han sidodestruidos.
Recomendaciones:Evitar el uso de hilos no asociados.Mover los hilos a otro alcance (via valor de retorno).Mover los hilos a un contenedor en un alcance mayor.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 41/52
Concurrencia en C++ moderno
Hilos
Terminación de hilos
Problemas con hilos no asociados
Incovenientes:Se pierde el control de qué hilos están activos.No se sabe si se puede usar el resultado generado por unhilo.No se sabe si un hilo ha liberado sus recursos.Se podría acabar accediendo a objetos que han sidodestruidos.
Recomendaciones:Evitar el uso de hilos no asociados.Mover los hilos a otro alcance (via valor de retorno).Mover los hilos a un contenedor en un alcance mayor.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 41/52
Concurrencia en C++ moderno
Exclusión mutua
1 La concurrencia está aquí
2 Concurrencia mínima
3 Hilos
4 Exclusión mutua
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 42/52
Concurrencia en C++ moderno
Exclusión mutua
Introducción
4 Exclusión mutuaIntroducciónMecanismos de espera
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 43/52
Concurrencia en C++ moderno
Exclusión mutua
Introducción
Exclusión mutua
mutex permite controlar el acceso con exclusión mutua aun recurso.
lock(): Adquiere el cerrojo asociado.unlock(): Libera el cerrojo asociado.
Ejemplo
mutex m;int x = 0;
void f () {m.lock() ;++x;m.unlock();
}
Hilos
void g() {thread t1( f ) ;thread t2( f ) ;
t1 . join () ;t2 . join () ;
cout << x << endl;}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 44/52
Concurrencia en C++ moderno
Exclusión mutua
Introducción
Exclusión mutua
mutex permite controlar el acceso con exclusión mutua aun recurso.
lock(): Adquiere el cerrojo asociado.unlock(): Libera el cerrojo asociado.
Ejemplo
mutex m;int x = 0;
void f () {m.lock() ;++x;m.unlock();
}
Hilos
void g() {thread t1( f ) ;thread t2( f ) ;
t1 . join () ;t2 . join () ;
cout << x << endl;}
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 44/52
Concurrencia en C++ moderno
Exclusión mutua
Introducción
Problemas con lock()/unlock()
Posibles problemas:Olvido de liberar cerrojo.Excepciones.
Solución unique_lock
mutex m;int x = 0;
void f () {unique_lock<mutex> l{m}; // Adquiere el cerrojo++x;
} // Libera el cerrojo
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 45/52
Concurrencia en C++ moderno
Exclusión mutua
Introducción
Problemas con lock()/unlock()
Posibles problemas:Olvido de liberar cerrojo.Excepciones.
Solución unique_lock
mutex m;int x = 0;
void f () {unique_lock<mutex> l{m}; // Adquiere el cerrojo++x;
} // Libera el cerrojo
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 45/52
Concurrencia en C++ moderno
Exclusión mutua
Introducción
Adquisición de múltiples mutex
La función lock() permite adquirir a la vez varios mutex.Adquiere todos o ninguno.Si alguno está bloqueado espera dejando libres todos.
mutex m1, m2, m3;void f () {
lock(m1, m2, m3);// Acceso a datos compartidos
} // Cuidado: No se liberan los cerrojos
Especialmente útil en cooperación con unique_lockvoid f () {
unique_lock<mutex> l1{m1, defer_lock}, l2{m2, defer_lock};lock( l1 , l2 ) ;// Acceso a datos compartidos
} // Ahora si se liberan los cerrojos
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 46/52
Concurrencia en C++ moderno
Exclusión mutua
Introducción
Adquisición de múltiples mutex
La función lock() permite adquirir a la vez varios mutex.Adquiere todos o ninguno.Si alguno está bloqueado espera dejando libres todos.
mutex m1, m2, m3;void f () {
lock(m1, m2, m3);// Acceso a datos compartidos
} // Cuidado: No se liberan los cerrojos
Especialmente útil en cooperación con unique_lockvoid f () {
unique_lock<mutex> l1{m1, defer_lock}, l2{m2, defer_lock};lock( l1 , l2 ) ;// Acceso a datos compartidos
} // Ahora si se liberan los cerrojos
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 46/52
Concurrencia en C++ moderno
Exclusión mutua
Mecanismos de espera
4 Exclusión mutuaIntroducciónMecanismos de espera
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 47/52
Concurrencia en C++ moderno
Exclusión mutua
Mecanismos de espera
Variables condición
Mecanismo para sincronizar hilos en acceso a recursoscompartidos.
wait(): Espera en un mutex.notify_one(): Despierta a un hilo en espera.notify_all(): Despierta a todos los hilos en espera.
Colas de peticiones
class peticion ;
queue<peticion> cola;condition_variable cv;mutex m;
void productor();void consumidor();
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 48/52
Concurrencia en C++ moderno
Exclusión mutua
Mecanismos de espera
Productor
void consumidor() {for (;;) {
unique_lock<mutex> l{m};
while (cv.wait( l ) ) ;
auto p = cola. front () ;cola.pop();l .unlock() ;
procesa(p);};
}
Efecto de wait:1 Libera el cerrojo y espera una notificación.2 Adquiere el cerrojo al despertarse.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 49/52
Concurrencia en C++ moderno
Exclusión mutua
Mecanismos de espera
Consumidor
void productor() {for (;;) {
peticion p = genera();
unique_lock<mutex> l{m};cola.push(p);
cv.notify_one() ;}
}
Efecto de notify_one():1 Despierta a uno de los hilos esperando en la condición.
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 50/52
Concurrencia en C++ moderno
Exclusión mutua
Mecanismos de espera
Resumen
The free lunch is over (o eso parece).C++ ofrece un mecanismo portable para concurrencia.Concurrencia mínima a través de tareas asíncronas yfuturos.Uso de hilos para control más detallado de laconcurrencia.Los mutex como mecanismo básico de concurrencia.Hay mucho más.
Usar una tarjeta gráfica sin usar CUDA u OpenCL?
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 51/52
Concurrencia en C++ moderno
Exclusión mutua
Mecanismos de espera
Concurrencia en C++ modernocodemotion 2014
J. Daniel Garcia
Grupo ARCOSUniversidad Carlos III de Madrid
21 de noviembre de 2014
cbed – J. Daniel Garcia – ARCOS@UC3M ([email protected] – @usingstdcpp – @jdgarciauc3m) 52/52