79
Taller de Programaci ´ on Paralela Concurrencia y nuevas caracter´ ısticas en C++11 Resumen trabajo de investigaci ´ on 1/2014 Universidad de Santiago de Chile Taller de Programaci´ on Paralela Profesor: Fernando Rannou Ayudante: Sebasti´ an Vizcay [email protected] Sebasti ´ an Vizcay June 16, 2015 1/79

Concurrencia y nuevas características en C++11

Embed Size (px)

Citation preview

Page 1: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Concurrencia y nuevas caracterısticas enC++11

Resumen trabajo deinvestigacion 1/2014

Universidad de Santiago de ChileTaller de Programacion ParalelaProfesor: Fernando RannouAyudante: Sebastian Vizcay

[email protected]

Sebastian Vizcay June 16, 2015 1/79

Page 2: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Concurrencia en C++11

Tabla de contenidos

I El header thread

I El header mutex

I El header condition variable

I El header atomix

I El header future

I Compilacion

Sebastian Vizcay June 16, 2015 2/79

Page 3: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Principales caracterısticas

I Comienza su ejecucion desde el momento en que se declara unavariable del tipo thread.

I La forma mas sencilla de indicar el codigo a ejecutar, es pasandole alconstructor el nombre de la funcion deseada.

I Otras alternativas son especificar un metodo de un objeto, unmetodo estatico de una clase,

I Se DEBE SIEMPRE decidir si se desea esperar a que la nueva hebrafinalice su trabajo o si es que se desea permitir que corra libremente.

Sebastian Vizcay June 16, 2015 3/79

Page 4: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Join o detach

I Si se desea esperar a que la nueva hebra finalice su trabajo antes dellamar al destructor, llamaremos al metodo join del objeto threadrecien creado.

I En caso en que se decide dejar a la nueva hebra correr de formacompletamente independiente, perrmitiendonos incluso a que lahebra principal finalice su trabajo y libere sus variables locales,llamaremos al metodo detach.

I En el caso en que se llame al metodo deatch, la nueva hebra deejecucion se desliga del objeto thread que recien creamos y secontinua ejecutando en background como un daemon.

Sebastian Vizcay June 16, 2015 4/79

Page 5: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Mas sobre detach

I Si se desliga la hebra de ejecucion del objeto thread con detach y lahebra principal ejecutando main finaliza, no se puede asegurar si lanueva hebra de ejecucion continuara ejecutandose.

I Caso especıfico: salidas a cout o a ficheros no son realizadas por lasegunda hebra si es que la primera finaliza la ejecucion de main.Tampoco es reportada su ejecucion con el comando ps H (comandoque muestra la lista de procesos/hebras ejecutandose).

I Acorde a cppreference.com, ejecucion de la segunda hebra deberıacontinuar independientemente.

Sebastian Vizcay June 16, 2015 5/79

Page 6: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Ejemplo basico

I #include <thread>

I Nombre de una funcion.

I Uso de join.

1 # inc lude <thread>2 void foo ( ) ;3 i n t main ( i n t argc , char ∗argv [ ] ) {4 std : : thread myThread ( foo ) ;5 myThread . j o i n ( ) ;6 r e t u r n 0 ;7 }

Sebastian Vizcay June 16, 2015 6/79

Page 7: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Segundo ejemplo: funcion con parametros

I NOTA: se envıan copias de los argumentos aun cuando el prototipode la funcion declare que recibe referencias como parametros.

1 # inc lude <thread>2 void foo ( i n t a , i n t b ) ;3 i n t main ( i n t argc , char ∗argv [ ] ) {4 i n t x = 5 , y = 10;5 s td : : thread myThread ( foo , x , y ) ;6 myThread . j o i n ( ) ;7 r e t u r n 0 ;8 }

Sebastian Vizcay June 16, 2015 7/79

Page 8: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Tercer ejemplo: metodo de un objeto

I Recordar que un metodo no es mas que una funcion que tiene comoprimer parametro implıcito un puntero constante al objeto llamadothis.

1 c lass MyClass {2 p u b l i c :3 MyClass ( ) ;4 ˜ MyClass ( ) ;5 method1 ( i n t x ) ;6 } ;

Sebastian Vizcay June 16, 2015 8/79

Page 9: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Tercer ejemplo: metodo de un objeto

I Recordar que un metodo no es mas que una funcion que tiene comoprimer parametro implıcito un puntero constante al objeto llamadothis.

1 # inc lude <thread>2 # inc lude ” myclass . hpp ”3 i n t main ( i n t argc , char ∗argv [ ] ) {4 MyClass myObject ( ) ;5 i n t number = 5;6 s td : : thread myThread (&MyClass : : method1 , &myObject , number ) ;7 myThread . j o i n ( ) ;8 r e t u r n 0 ;9 }

Sebastian Vizcay June 16, 2015 9/79

Page 10: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Cuarto ejemplo: funcion con parametros de tipo referencias

I #include <functional>para usar las funciones ref y cref.

I Usar ref para enviar la variable por referencia y cref para cuando lareferencia fue declarada como const.

1 # inc lude <thread>2 # inc lude <f u n c t i o n a l>3 void foo ( i n t &a , const i n t &b ) ;4 i n t main ( i n t argc , char ∗argv [ ] ) {5 i n t x = 5 , y = 10;6 s td : : thread myThread ( foo , s td : : r e f ( x ) , s td : : c r e f ( y ) ) ;7 myThread . j o i n ( ) ;8 r e t u r n 0 ;9 }

Sebastian Vizcay June 16, 2015 10/79

Page 11: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Quinto ejemplo: crear un objeto con hebra de ejecucion propia

I Se debe declarar un atributo miembro del tipo thread dentro de laclase.

I Se llama al constructor de thread, indicando el metodo a ejecutarutilizando la nueva hebra de ejecucion, al momento en que seconstruye un objeto de la clase.

I Se invoca al metodo join del objeto thread dentro del destructor dela clase, el cual sera ejecutado una vez que se acabe el scope delobjeto concurrente o a traves de una llamada explıcita a delete.

Sebastian Vizcay June 16, 2015 11/79

Page 12: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Quinto ejemplo: crear un objeto con hebra de ejecucion propia

1 # inc lude <thread>23 c lass Task {4 p u b l i c :5 Task ( ) ;6 ˜ Task ( ) ;78 p r i v a t e :9 s td : : thread pr iva teThread ;

10 void main ( ) ;11 } ;

Sebastian Vizcay June 16, 2015 12/79

Page 13: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Quinto ejemplo: crear un objeto con hebra de ejecucion propia

1 # inc lude ” task . hpp ”23 Task : : Task ( ) {4 pr iva teThread = std : : thread (&Task : : main , t h i s ) ;5 }67 Task : : ˜ Task ( ) {8 pr iva teThread . j o i n ( ) ;9 }

Sebastian Vizcay June 16, 2015 13/79

Page 14: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Que es lo que se envıa realmente al constructor de la clase thread?

I Cuando se crea un objeto thread y se indica una funcion en conjuntocon los argumentos que necesita, lo que se envıa realmente alconstructor es la funcion retornada por la utilidad bind.

I Bind es una utilidad que nos permite generar funciones a partir deotra funcion, la cual es utilizada como plantilla.

I Bind puede ligar valores a los parametros que son esperados por lafuncion plantilla.

Sebastian Vizcay June 16, 2015 14/79

Page 15: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Usando bind para generar funciones

1 i n t sum( i n t a , i n t b ) {2 r e t u r n a + b ;3 }45 i n t sum34 ( ) {6 r e t u r n 3 + 4;7 }

1 i n t sumA4( i n t a ) {2 r e t u r n a + 4;3 }45 i n t sum3B( i n t b ) {6 r e t u r n 3 + b ;7 }

Sebastian Vizcay June 16, 2015 15/79

Page 16: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Usando bind para generar funciones

1 # inc lude <f u n c t i o n a l>2 i n t main ( i n t argc , char ∗argv [ ] ) {3 auto bindSum34 = std : : b ind (sum, 3 , 4 ) ;4 auto bindSumA4 = std : : b ind (sum, s td : : p laceho lders : : 1 , 4 ) ;5 auto bindSum3B = std : : b ind (sum, 3 , s td : : p laceho lders : : 1 ) ;6 s td : : cout << sum(3 , 4) << std : : endl ;7 s td : : cout << bindSum34 ( ) << s td : : endl ;8 s td : : cout << bindSumA4 ( 3 ) << s td : : endl ;9 s td : : cout << bindSum3B ( 4 ) << s td : : endl ;

1011 r e t u r n 0 ;12 }

Sebastian Vizcay June 16, 2015 16/79

Page 17: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Comentarios adicionales sobre bind

I Tanto bind como placeholders estan declarados en el headerfunctional.

I Se pueden especificar placeholders adicionales. 1 hace referenciaal primer parametro, 2 al segundo y ası sucesivamente.

I Las variables que reciben la funcion retornada por bind funcionanexactamente igual que las funciones ordinarias, y deben invocarseespecificando una lista de argumentos encerrada entre parentesis.

Sebastian Vizcay June 16, 2015 17/79

Page 18: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Comentarios adicionales sobre bind

I No necesitamos explicitar el tipo de valor retornado por bind en elmomento de declarar las variables. Una de las nuevascaracterısticas de C++11 es el uso de auto para dejar al compiladorque realice estas detecciones.

I El uso de bind es util para modificar el signature de funciones a lascuales no tenemos acceso para modificarlas, como por ejemploalgoritmos provistos por la stl en el header algorithms.

Sebastian Vizcay June 16, 2015 18/79

Page 19: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Identificando a una hebra

I Ademas de los metodos join y detach, los objetos del tipo threadposeen un tercer metodo que resulta de interes, el metodo get id.

I El metodo get id retorno un identificador unico de la hebra deejecucion.

I ID retornado resulta ser una secuencia numerica larga pocoamigable para ser recordada.

I El ID retornado por get id puede ser reutilizado a medida en que sevan creando y destruyendo las hebras de ejecucion.

I Se aconseja asignar manualmente IDs unicos a cada thread,agregando a la lista de parametros una variable numerica que seautilizada como identificador.

Sebastian Vizcay June 16, 2015 19/79

Page 20: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Identificando a una hebra

I La funcion get id del scope this thread no es mas que una funcionque invoca al metodo get id del objeto thread actualmenteejecutando la lınea particular de codigo.

1 # inc lude <thread>2 void foo ( i n t t i d ) ;3 i n t main ( i n t argc , char ∗argv [ ] ) {4 i n t i d = 0 ;5 s td : : thread myThread ( foo , i d ) ;6 myThread . j o i n ( ) ;78 r e t u r n 0 ;9 }

Sebastian Vizcay June 16, 2015 20/79

Page 21: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Identificando a una hebra

1 void foo ( i n t t i d ) {2 std : : cout << ”my i d : ” << std : : t h i s t h r e a d : : g e t i d ( ) ;3 s td : : cout << std : : endl ;4 s td : : cout << ” manual i d : ” << t i d << s td : : endl ;5 }

Listing 1: output

1 my i d : 1401501556304642 manual i d : 0

Sebastian Vizcay June 16, 2015 21/79

Page 22: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Otras utilidades del namespace this thread

I El namespace this thread agrupa un conjunto de funciones basicasque resultan ser utiles para mecanismos de planificacion de hebras.

I Ademas de la funcion get id, se encuentran las funciones sleep for,sleep until y yield.

I Todas estas funciones hacen referencia a la hebra actual que seencuentra ejecutandose.

Sebastian Vizcay June 16, 2015 22/79

Page 23: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Otras utilidades del namespace this thread

I La funcion yield sirve para indicar a la implementacion quebuscamos que se realice una replanificacion del scheduling dehebras, es decir, se da la oportunidad a que se ejecuten otrashebras.

I No se puede especificar hebras en particular (la funcion no recibeningun parametro).

I No se asegura que otra hebra entre a ejecutarse. Yield debe versecomo una sugerencia de replanificacion.

Sebastian Vizcay June 16, 2015 23/79

Page 24: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Otras utilidades del namespace this thread

I Uso de sleep y usleep para suspender la ejecucion de una hebra esconsiderado obsoleto.

I Uso del namespace chrono para definir periodos de tiempo de formaflexible (precision de segundos,minutos, horas, microsegundos,nanosegundos, etc.).

I Funciones sleep for y sleep until toman como argumento unperiodo de tiempo y un instante en el tiempo respectivamente. Elprimero es un tiempo relativo al tiempo exacto en que se invoca asleep for y el segundo es un tiempo absoluto, independiente deltiempo en que se invoco a sleep until.

Sebastian Vizcay June 16, 2015 24/79

Page 25: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Ejemplo de uso de sleep for

1 # inc lude <chrono>2 # inc lude <thread>34 i n t main ( i n t argc , char ∗argv [ ] ) {5 std : : chrono : : m i l l i seconds dura t i on (5000) ; / / 5 seconds6 std : : t h i s t h r e a d : : s l e e p f o r ( du ra t i on ) ;78 r e t u r n 0 ;9 }

Sebastian Vizcay June 16, 2015 25/79

Page 26: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Thread

Consideracion con respecto a locks y similares

I Si se esta trabajando con variables de condicion o locks, el invocara yield o a a algun tipo de sleep no liberara el lock del mutex que sehabıa adquirido, por lo que se debe tener cuidado a la hora de llamara estas funciones habiendo adquirido algun lock previamente.

Sebastian Vizcay June 16, 2015 26/79

Page 27: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Exclusion mutua - Mutex

I Para hacer uso de exclusion mutua, se debe incluir el header<mutex>.

I Ejemplo de uso: deseamos obtener una salida limpia en cout, esdecir, que no se mezcle la escritura de hebras concurrentes.

Sebastian Vizcay June 16, 2015 27/79

Page 28: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Ejemplo basico

1 # inc lude <mutex>2 void p r i n t ( i n t value1 , i n t value2 , i n t value3 ) {3 s t a t i c s td : : mutex m;4 m. lock ( ) ;5 s td : : cout << value1 << ” ” ;6 s td : : cout << value2 << ” ” ;7 s td : : cout << value3 << ” ” ;8 s td : : cout << s td : : endl ;9 m. unlock ( ) ;

10 }

Sebastian Vizcay June 16, 2015 28/79

Page 29: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Analizando el ejemplo anterior

I Cada vez que una hebra intenta invocar la funcion print, estaintentara adquirir el lock del mutex.

I Una vez que una hebra adquiere el lock, esta entra a lo que esconocido como seccion crıtica. La seccion crıtica en este caso sonlas lıneas de codigo que imprimen los 3 valores en la pantalla.

I Ninguna otra hebra puede entrar a la seccion crıtica mientras el lockse encuentre tomado.

I Una vez que la hebra que adquirio el lock termina de ejecutar laseccion crıtica, esta libera el lock manualmente realizando ununlock.

Sebastian Vizcay June 16, 2015 29/79

Page 30: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Analizando el ejemplo anterior

I NOTA: la variable mutex, la cual provee los metodos lock y unlock, esdeclarada static con el fin de que se cree solo una variable del tipomutex comun para todas las invocaciones a print.

I Cuando se hace uso de locks se serializa el codigo. Atomix es unaalternativa al uso de locks pero solo algunos tipos de variablespermiten ser manejados a nivel atomico, ademas de ser solo algunasoperaciones las que efectivamente se permiten que se realicen deforma atomica.

I Notar que si llega ocurrir un error dentro de la seccion crıtica, lahebra que se encuentre ejecutando la seccion crıtica nuncaalcanzara a ejecutar el unlock del mutex, produciendo que laaplicacion se cuelge completamente.

Sebastian Vizcay June 16, 2015 30/79

Page 31: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Ejemplo 2: Alternativa try-catch

1 # inc lude <mutex>2 # inc lude <except ion>3 void p r i n t ( i n t value1 , i n t value2 , i n t value3 ) {4 s t a t i c s td : : mutex m;5 m. lock ( ) ;6 t r y {7 / / c r i t i c a l sec t ion8 m. unlock ( ) ;9 } catch ( s td : : except ion &e ) {

10 m. unlock ( ) ;11 }12 }

Sebastian Vizcay June 16, 2015 31/79

Page 32: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Mutex con RAII

I Una alternativa mejor serıa que el lock se liberase automaticamenteuna vez que finaliza el scope del mutex. Ası el programador notendrıa que preocuparse de liberar manualmente el lock poniendoseen cada escenario en que el bloque de codigo de la seccion crıticapudiese fallar.

I Dentro de C++11 se sugiere el uso de manejadores de locks. RAII(Resource Acquisition Is Initialization) es un termino que se refiere aque la adquisicion de un recurso esta limitada por el tiempo de vidadel objeto que lo adquiere. Este idea tambien puede ser encontradacomo CADRe (Constructor Acquires, Destructor Release).

I RAII provee encapsulacion y seguridad en caso que ocurranexcepciones.

Sebastian Vizcay June 16, 2015 32/79

Page 33: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Ejemplo 3: Uso de lock guard como manejador de locks

1 # inc lude <mutex>2 void p r i n t ( i n t value1 , i n t value2 , i n t value3 ) {3 s t a t i c s td : : mutex m;4 std : : lock guard<std : : mutex> l ock (m) ;5 / / c r i t i c a l sec t ion6 }

Sebastian Vizcay June 16, 2015 33/79

Page 34: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Usando lock guard

I El constructor de lock guard invoca al metodo lock del mutex.

I En el instante en que se acaba el scope del objeto lock guard oalguna excepcion es arrojada, el destructor del lock guard realizauna llamada al metodo unlock del mutex.

Sebastian Vizcay June 16, 2015 34/79

Page 35: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Usando unique lock

I Existe otro manejador de mutex aparte del lock guard, llamadounique lock. La diferencia entre unique lock y guard lock, es que elultimo, solo permite la invocacion del lock del mutex al momento deconstruccion del objeto guard lock, es decir, el manejadorunique lock es mas flexible permitiendo mayor control sobre lainvocacion de los metodos lock y unlock.

I Un objeto del tipo unique lock permite ser contruido sin necesidad dellamar al metodo lock del mutex interno. De todas formas se aseguraque al momento en que se acaba su scope, el mutex se encontraraliberado.

Sebastian Vizcay June 16, 2015 35/79

Page 36: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Usando unique lock

I Cuando se utilizan variables de condicion es necesario tener queliberar el lock antes de que la hebra se bloquee al tener que ejecutarel wait. El uso de guard lock resulta ser no suficiente para talescasos debido a que se deben especificar locks y unlocks adicionalesa los momento en que se construye y destruye el manejador delocks.

I Como regla, solo cuando se realicen llamados explıcitos al metodolock del mutex se debera tambien llamar explıcitamente al metodounlock.

Sebastian Vizcay June 16, 2015 36/79

Page 37: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Tipos de mutex

Mutex: El mutex tradicional. Si una misma hebra llama al metodolock de un mutex dos veces consecutivas sin hacer ununlock, la segunda llamada producira un deadlock.

Recursive mutex: Permite que el lock sea obtenido mas de una vez por lamisma hebra (similar a la caracterıstica de exclusion mutuaen µC++).

Sebastian Vizcay June 16, 2015 37/79

Page 38: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Tipos de mutex

Timed mutex: Mutex que ademas de los metodos lock y unlock, proveentambien los metodos try lock for y try lock until, loscuales intentan durante una determinada cantidad detiempo adquirir el lock. Ambos metodos retornan despuesde tal periodo independientemente de si lograron o noadquirirlo. En caso en que la adquisicion fue existosa, losmetodos retornan verdadero y cierran el lock (recordarhacer el posterior unlock). En caso que expire el timeout,los metodos retornan falso y no hay nada que se necesiteliberar.

Recursive timed mutex: Es la union de las dos caracterısticas anteriores.

Sebastian Vizcay June 16, 2015 38/79

Page 39: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Spinlock con try lock

I El metodo try lock es comun a todos los tipos de mutex. Estemetodo puede ser utilizado para hacer un spinlock.

I Recordar que cuando una hebra encuentra que el lock esta tomado,esta se bloquea esperando a que se libere el lock. Un spinlock, envez de bloquear a la hebra, comienza a hacer lo que es conocidocomo busy-waiting.

Sebastian Vizcay June 16, 2015 39/79

Page 40: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Spinlock con try lock

1 # inc lude <mutex>2 i n t main ( i n t argc , char ∗argv [ ] ) {3 std : : mutex mutex ;4 wh i le ( ! mutex . t r y l o c k ( ) ) {5 ; / / busy−wa i t i ng6 }7 / / c r i t i c a l sec t ion8 mutex . unlock ( ) ;9

10 r e t u r n 0 ;11 }

Sebastian Vizcay June 16, 2015 40/79

Page 41: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Table : Resumen tipo de mutex y metodos

Metodo Mutex Recursive mutex Timed mutex Recursive timed mutexlock X X X Xunlock X X X Xtry lock X X X Xtry lock for X Xtry lock until X X

Sebastian Vizcay June 16, 2015 41/79

Page 42: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Table : Resumen tipo de manejadores de mutex y metodos

Metodo Lock guard Unique locklock Xunlock Xtry lock Xtry lock for Xtry lock until X

I Lock guard obviamente no posee ningun metodo dado que el lock yunlock lo realiza al instante en que se construye y destruye el objeto.Unique lock posee todos los metodos pero para invocar a los dosultimos, el mutex manejado debe ser del tipo timed.

Sebastian Vizcay June 16, 2015 42/79

Page 43: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Simulando un monitor de µC++

1 # inc lude <mutex>23 c lass Moni tor {4 p u b l i c :5 Moni tor ( ) ;6 ˜ Moni tor ( ) ;78 p r i v a t e :9 s td : : recurs ive mutex mutex ;

1011 void method1 ( ) ;12 vo id method2 ( ) ;13 } ;

Sebastian Vizcay June 16, 2015 43/79

Page 44: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Simulando un monitor de µC++

1 # inc lude ” moni tor . hpp ”23 void Moni tor : : method1 ( ) {4 std : : un ique lock<std : : recurs ive mutex> l ock ( mutex ) ;5 / / c r i t i c a l sec t ion6 }78 void Moni tor : : method2 ( ) {9 std : : un ique lock<std : : recurs ive mutex> l ock ( mutex ) ;

10 / / c r i t i c a l sec t ion11 }

Sebastian Vizcay June 16, 2015 44/79

Page 45: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Funciones adicionales

I Dentro del header mutex se pueden encontrar las siguientesfunciones adicionales: lock, try lock, call once.

I La funcion lock toma como parametro una lista de objetos del tipomutex de los cuales intentara obtener el lock. En caso en que no selogren obtener todos los locks, la funcion realizara un rollbackquedando todos los locks nuevamente liberados. En caso de exito, lafuncion retornara y la hebra podra acceder a la seccion crıtica. Unavez que se ejecuto la seccion crıtica, se deben liberar de formamanual todos los locks que se obtuvieron.

Sebastian Vizcay June 16, 2015 45/79

Page 46: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Uso de la funcion lock

I El orden en que se especifican los mutex no es importante. Lafuncion fue disenada de tal forma para aliviar al programador el tenerque escribir la adquisicion de locks en un determinado orden con elfin evitar deadlocks.

1 # inc lude <mutex>2 std : : mutex mutex1 , mutex2 ;3 vo id task1 ( ) {4 std : : lock ( mutex1 , mutex2 ) ;5 / / c r i t i c a l sec t ion6 mutex1 . unlock ( ) ;7 mutex2 . unlock ( ) ;8 }

123 void task2 ( ) {4 std : : lock ( mutex2 , mutex1 ) ;5 / / c r i t i c a l sec t ion6 mutex1 . unlock ( ) ;7 mutex2 . unlock ( ) ;8 }

Sebastian Vizcay June 16, 2015 46/79

Page 47: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Funciones adicionales

I La funcion try lock funciona de forma similar a la funcion lock peroinvocara al metodo try lock de todos los mutex que se le indiquendentro de la lista de parametros.

I Try lock retornara -1 en caso en que obtenga todos los lockssatisfatoriamente o un numero entero indicando el ındice del primermutex del cual no logro adquirir su lock.

Sebastian Vizcay June 16, 2015 47/79

Page 48: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Funciones adicionales: call once y once flag

I Si tenemos una funcion que sera ejecutada por multiples hebras ydeseamos que cierta parte de la funcion sea solo ejecutada por tansolo una hebra, podemos utilizar la funcion call once paraespecificar las lıneas de codigo que deben ejecutarse solo una vez.

I La funcion call once recibe como parametro una funcion, un objetofuncional, un lambda o cualquer otro objeto que se comporte comofuncion para indicar el codigo a ejecutar.

I Call once necesita verificar una variable del tipo once flag para versi el codigo ya fue o no ejecutado por alguna otra hebra.

Sebastian Vizcay June 16, 2015 48/79

Page 49: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Mutex

Uso de call once

1 # inc lude <mutex>23 std : : once f lag f l a g ;45 void task ( ) {6 std : : ca l l once ( f l ag , [ ] ( ) {7 std : : cout << ” p r i n t j u s t once ” << s td : : endl ;8 } ) ;9 s td : : cout << ” p r i n t every s i n g l e t ime ” << s td : : endl ;

10 }

Sebastian Vizcay June 16, 2015 49/79

Page 50: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Caracterısticas principales

I #include <condition variable>

I Las variables de condicion manejan una lista de hebras que seencuentran a la espera de que otra hebra notifique algun evento.

I Toda hebra que necesita esperar por alguna variable de condicion,DEBE haber adquirido previamente el lock.

I El lock es liberado una vez que la hebra comienza a esperar en lavariable de condicion.

I El lock es obtenido nuevamente una vez que la hebra essenalizada para continuar.

Sebastian Vizcay June 16, 2015 50/79

Page 51: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Caracterısticas principales

I El mutex DEBE ser manejado por el wrapper unique lock.

I Si se desea utilizar otro tipo de wrapper, se debe utilizar unavariable de condicion del tipo condition variable any.

Sebastian Vizcay June 16, 2015 51/79

Page 52: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Caracterısticas principales

I Los metodos principales son wait, notify one y notify all.

I El metodo wait bloquea a la hebra en espera del evento en particular.

I El metodo notify one despertara a solo una hebra que se encuentreen la cola de hebras esperando por el evento que representa lavariable de condicion. En caso en que no haya ninguna hebraesperando, no sucede nada en particular.

I El metodo notify all despertara a todas las hebras esperando por talevento. Notar que las hebras despertadas aun tendran que tomar ellock nuevamente, por lo que la ejecucion de ellas y su acceso a laseccion critica sigue siendo serializado. De igual forma a notify one,si no existe ninguna hebra en la cola de espera, no sucedera nadaespecial.

Sebastian Vizcay June 16, 2015 52/79

Page 53: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Caracterısticas principales

I El metodo wait toma como primer parametro obligatorio el mutex delcual debe liberar el lock antes de bloquearse.

I El segundo parametro es opcional y corresponde a un predicado(algo que puede evaluarse a true o false) que indicara si la hebradebe efectivamente despertar o seguir bloqueada aun cuando sehaya despertado.

I El especificar un predicado es equivalente a envolver la llamada await con un while verificando por la negacion del predicado.

Sebastian Vizcay June 16, 2015 53/79

Page 54: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Especificando un predicado

1 std : : mutex mutex ;2 s td : : un ique lock<std : : mutex> l ock ( mutex ) ;3 s td : : c o n d i t i o n v a r i a b l e inser tOne ;4 wh i le ( counter == 0) {5 inser tOne . wa i t ( lock ) ;6 }

1 std : : mutex mutex ;2 s td : : un ique lock<std : : mutex> l ock ( mutex ) ;3 s td : : c o n d i t i o n v a r i a b l e inser tOne ;4 inser tOne . wa i t ( lock , [ counter ] ( ) −> bool {5 r e t u r n counter != 0} ) ;

Sebastian Vizcay June 16, 2015 54/79

Page 55: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Spurious Wakes

I Cuando no se especifica un predicado en el segundo parametro delwait, hay que tener cuidado de envolver la llamada a wait con unwhile y no con una simple instruccion if.

I Una hebra puede despertar aun cuando no se haya realizadoninguna senalizacion a traves de algun notify. Esto es conocidocomo spurious wakes (despertar erroneo) y no pueden serpredichos. Spurious wakes generalmente ocurren cuando labiblioteca de hebras no puede asegurar que la hebra dormida no seperdera de alguna notificacion, por lo que decide despertarla paraevitar el riesgo.

I Al utilizar un while o un predicado, nos aseguramos que la hebradespierta solamente cuando corresponde.

Sebastian Vizcay June 16, 2015 55/79

Page 56: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Ejemplo completo de variables de condicion

1 c lass Bu f fe r {2 p u b l i c :3 Bu f fe r ( unsigned s i z e = 0 ) ;4 ˜ Bu f fe r ( ) ;5 vo id i n s e r t ( i n t i tem ) ;6 i n t remove ( ) ;7 p r i v a t e :8 unsigned s ize ;9 unsigned counter ;

10 i n t ∗ i tems ;11 std : : c o n d i t i o n a l v a r i a b l e removeOne , inser tOne ;12 std : : mutex mutex ;13 } ;

Sebastian Vizcay June 16, 2015 56/79

Page 57: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Ejemplo completo de variables de condicion

1 # inc lude ” b u f f e r . hpp ”2 Bu f fe r : : Bu f fe r ( unsigned s i z e ) : s i ze ( s i z e ) ,3 counter ( 0 ) , i tems (new i n t [ s i z e ] ) {}45 Bu f fe r : : ˜ Bu f fe r ( ) {6 de le te [ ] i tems ;7 }

Sebastian Vizcay June 16, 2015 57/79

Page 58: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Ejemplo completo de variables de condicion

1 void Bu f fe r : : i n s e r t ( i n t i tem ) {2 std : : un ique lock<std : : mutex> l ock ( mutex ) ;3 removeOne . wa i t ( lock , [ t h i s ] ( ) −> bool {4 r e t u r n counter != s ize ; } ) ;5 i tems [ counter ] = i tem ;6 counter ++;78 inser tOne . n o t i f y o n e ( ) ;9 }

Sebastian Vizcay June 16, 2015 58/79

Page 59: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Ejemplo completo de variables de condicion

1 void Bu f fe r : : remove ( ) {2 std : : un ique lock<std : : mutex> l ock ( mutex ) ;3 wh i le ( counter == 0) {4 inser tOne . wa i t ( lock ) ;5 }6 counter−−;7 i n t i tem = items [ counter ] ;89 removeOne . n o t i f y o n e ( ) ;

1011 r e t u r n i tem ;12 }

Sebastian Vizcay June 16, 2015 59/79

Page 60: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Ejemplo completo de variables de condicion

1 # inc lude <thread>2 # inc lude ” b u f f e r . hpp ”34 void consumer ( i n t t i d , Bu f fe r & b u f f e r ) {5 i n t value = b u f f e r . remove ( ) ;6 s td : : cout << ” thread= ” << t i d ;7 s td : : cout << ” took the value ” ;8 s td : : cout << value << std : : endl ;9 }

1011 void producer ( i n t t i d , Bu f fe r & b u f f e r ) {12 b u f f e r . i n s e r t ( t i d ) ;13 }

Sebastian Vizcay June 16, 2015 60/79

Page 61: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Condition variable

Ejemplo completo de variables de condicion

1 i n t main ( i n t argc , char ∗argv [ ] ) {2 Bu f fe r b u f f e r ( 5 ) ;3 s td : : thread consumers [1000 ] , producers [ 1 0 0 0 ] ;45 f o r ( unsigned i = 0 ; i < 1000; i ++) {6 consumers [ i ] = s td : : thread ( consumer , i , s td : : r e f ( b u f f e r ) ) ;7 producers [ i ] = s td : : thread ( producer , i , s td : : r e f ( b u f f e r ) ) ;8 }9 f o r ( unsigned i = 0 ; i < 1000; i ++) {

10 consumers [ i ] . j o i n ( ) ;11 producers [ i ] . j o i n ( ) ;12 }13 r e t u r n 0 ;14 }

Sebastian Vizcay June 16, 2015 61/79

Page 62: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Atomic

Caracterısticas principales

I #include <atomic>.

I Atomic permite la implementacion de algoritmos libres de locks.

I Operaciones atomicas presentan mejor performance que suequivalente utilizando locks.

I Uno debe especificar el tipo de variable que sera tratada de formaatomica (thread-safe).

I Va a depender de la implementacion el mecanismo utilizado paraasegurar que la operacion sea thread-safe, pudiendo incluso llegar autilizarse locks. Generalmente para tipos de datos basicos, lasimplementaciones quedan libres de locks.

Sebastian Vizcay June 16, 2015 62/79

Page 63: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Atomic

Caracterısticas principales

I Los metodos principales son load y store.

I El metodo load funciona como los metodos get y simplementeretorna el valor actual. El metodo store setea un nuevo valor a lavariable del tipo atomic.

I Existe otro metodo llamado exchange, el cual setea un nuevo valor ala variable atomica y al mismo tiempo retorna el valor antiguo.

Sebastian Vizcay June 16, 2015 63/79

Page 64: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Atomic

Ejemplo de atomic

1 # inc lude <atomic>2 i n t main ( i n t argc , char ∗argv [ ] ) {3 i n t i n i t i a l V a l u e = 5;4 s td : : atomic<i n t> atomic ( i n i t i a l V a l u e ) ;5 i n t newValue = 10;6 atomic . s to re ( newValue ) ;7 i n t l as tVa lue = atomic . load ( ) ;89 r e t u r n 0 ;

10 }

Sebastian Vizcay June 16, 2015 64/79

Page 65: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Atomic

Caracterısticas principales

I Los metodos load y store permiten ademas especificar el modelo dememoria a utilizar (secuencialmente consistente es el modelo pordefecto).

I Los modelos de memoria describen si los accesos a memoriapueden ser optimizados ya sea por el compilador o por el procesador.Si el modelo es relajado, se permite que las operaciones seanreordenadas o combinadas con el fin de mejorar el performance.

I El modelo mas estricto es el secuencialmente consistente, el cual esel por defecto.

Sebastian Vizcay June 16, 2015 65/79

Page 66: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Atomic

Ejemplo del metodo exchange

1 # inc lude <atomic>2 std : : atomic<bool> winner ( f a l s e ) ;34 vo id race ( ) {5 i f ( ! winner . exchange ( t r ue ) ) {6 std : : cout << ” winner i d = ” ;7 s td : : cout << s td : : t h i s t h r e a d : : g e t i d ( ) << s td : : endl ;8 }9 }

Sebastian Vizcay June 16, 2015 66/79

Page 67: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Atomic

Ejemplo del metodo exchange

1 # inc lude <thread>23 i n t main ( i n t argc , char ∗argv [ ] ) {4 std : : thread threads [ 1 0 ] ;56 f o r ( unsigned i = 0 ; i < 10; i ++) {7 threads [ i ] = s td : : thread ( race ) ;8 }9

10 f o r ( unsigned i = 0 ; i < 10; i ++) {11 threads [ i ] . j o i n ( ) ;12 }1314 r e t u r n 0 ;15 } Sebastian Vizcay June 16, 2015 67/79

Page 68: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Comunicacion asıncrona con Future

I #include <future>.

I La funcion async nos permite lanzar la ejecucion de una hebra sinque necesariamente comience su ejecucion inmediatamente.

I Si la funcion que lanzamos asıncronamente retornar algun valor,podemos consultar por el valor retornado almacenandolo en unavariable del tipo future.

I La clase future es una clase template (de igual forma que atomic), endonde se debe especificar el tipo de dato que sera utilizado en eltemplate. El tipo de dato tiene que corresponderse con el tipo dedato retornado por la funcion lanzada asıncronamente.

Sebastian Vizcay June 16, 2015 68/79

Page 69: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Comunicacion asıncrona con Future

I En general se puede ver el header future como un conjunto deutilidades que permiten acceso asıncrono a valores que seranseteados por proveedores corriendo en otras hebras de ejecucion.

I El metodo get es el metodo principal de los objetos del tipo future. Elmetodo get es el que obliga a que el valor sea finalmentecalculado/seteado en caso en que todavıa no lo haya sido. En elcaso en que el valor todavıa no se encuentre disponible, la hebraejecutando el metodo get se bloqueara hasta que el valor seencuentre disponible.

I La hebra calculando el valor puede setear una excepcion en el valora retornar, lanzandose efectivamente al momento en que se realizala llamada a get.

Sebastian Vizcay June 16, 2015 69/79

Page 70: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Ejemplo de future

1 # inc lude <f u tu re>23 i n t add ( i n t a , i n t b ) {4 r e t u r n a+b ;5 }67 i n t main ( i n t argc , char ∗argv [ ] ) {8 std : : f u tu re<i n t> r e s u l t ( s td : : async ( add , 2 , 4) ) ;9

10 std : : cout << r e s u l t . get ( ) << std : : endl ;1112 r e t u r n 0 ;13 }

Sebastian Vizcay June 16, 2015 70/79

Page 71: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Proveedores para future

I La funcion async es la mas facil de utilizar para proveer de valores alas variables del tipo future.

I Ademas de la funcion async, se cuenta con los objetos de las clasespromise y packaged task.

I La clase promise es una clase template que permite proveer devalores o de excepciones a variables del tipo future.

I Los metodos principales de un objeto promise son get future,set value y set exception.

Sebastian Vizcay June 16, 2015 71/79

Page 72: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Proveedores para future - promise

I El metodo get future es la forma en la que se vincula el future conel promise.

I Se debe invocar al metodo get future cuando se estaconstruyendo/incializando el objeto del tipo future. Posterior a estainvocacion, el objeto del tipo promise y future poseeran un estadocompartido.

I Solo puede retornarse un future por promise.

I Una vez que se ha generado el vınculo, se espera que en algunmomento el promise setee el valor esperado por el future, o quesimplemente setee alguna excepcion para que sea lanzada por elfuture al intentar recuperar el valor con el metodo get.

Sebastian Vizcay June 16, 2015 72/79

Page 73: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Proveedores para future - promise

I Si se llegase a ejecutar el destructor de la variable de tipo promisesin que esta haya alcanzado a setear algun valor o excepcion, elestado de la informacion pasara a estar como disponible y lainvocacion del metodo get del future retornara. En el momento enque retorne get, se lanzara una excepcion del tipo future error.

I Cuando en alguna hebra de ejecucion se invoca a una de losmetodos set value o set exception, el estado cambia a disponible yla hebra bloqueada en la invocacion a get retorna.

Sebastian Vizcay June 16, 2015 73/79

Page 74: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Ejemplo de future y promise

1 # inc lude <f u tu re>2 # inc lude <thread>3 # inc lude <except ion>45 void p r i n t ( s td : : f u tu re<i n t> & f u t u r e ) {6 t r y {7 std : : cout << f u t u r e . get ( ) << std : : endl ;8 } catch ( s td : : except ion & e ) {9 std : : ce r r << e . what ( ) << std : : endl ;

10 }11 }

Sebastian Vizcay June 16, 2015 74/79

Page 75: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Ejemplo de future y promise

1 i n t main ( i n t argc , char ∗argv [ ] ) {2 std : : promise<i n t> promise ;3 s td : : f u tu re<i n t> f u t u r e ( promise . g e t f u t u r e ( ) ) ;4 s td : : thread thread ( p r i n t , s td : : r e f ( f u t u r e ) ) ;5 promise . se t va lue ( 1 5 ) ;67 thread . j o i n ( ) ;89 r e t u r n 0 ;

10 }

Sebastian Vizcay June 16, 2015 75/79

Page 76: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Proveedores para future - packaged task

I La clase packaged task funciona de forma similar al promise perose diferencian en que la primera envuelve a un objeto llamable (unafuncion, una expresion lambda, un functor etc.).

I El valor retornado por el objeto funcional es lo que se setea comovalor a almacenar en el future, es decir, ya no es necesarioespecificar el valor a traves de alguna llamada a set value como sehacıa en los objetos promise.

I La clase packaged task es una clase template y dentro de losparametros del template se debe especificar el tipo de retorno de lafuncion y los tipos de variables de los parametros.

Sebastian Vizcay June 16, 2015 76/79

Page 77: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Future

Ejemplo de future y packaged task

1 i n t add ( i n t a , i n t b ) {2 r e t u r n a+b ;3 }45 i n t main ( i n t argc , char ∗argv [ ] ) {6 std : : packaged task<i n t ( i n t , i n t )> task ( add ) ;7 s td : : f u tu re<i n t> f u t u r e ( task . g e t f u t u r e ( ) ) ;8 s td : : thread thread ( s td : : move( task ) , 3 , 4 ) ;9 s td : : cout << f u t u r e . get ( ) << s td : : endl ;

10 thread . j o i n ( ) ;11 r e t u r n 0 ;12 }

Sebastian Vizcay June 16, 2015 77/79

Page 78: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Compilacion

I C++11 utilza hebras provistas por la plataforma.I pthreads Unix.I win32 threads Windows

I Consultar por la biblioteca de hebras utilizadas por el compilador.I $ g++ -v (buscar por la lınea Thread model).

I Compilar con flag -std=c++11 o -std=c++0x, y enlazar con pthreads.I g++ -c -Wall -std=c++11 -o main.o main.cppI g++ -o program.exe main.o -pthreads

I En caso de error al enlazar con pthread.I g++ -o program.exe main.o -pthreads -Wl,–no-as-needed

Sebastian Vizcay June 16, 2015 78/79

Page 79: Concurrencia y nuevas características en C++11

Taller deProgramacion Paralela

Consultas

Sebastian Vizcay June 16, 2015 79/79