Bienvenidos sean a este post, hoy veremos como intercambiar informacion de un thread a otro.
Ya hemos visto en este post como implementar a thread y asociarlos a una funcion. Pero una cosa que no mencionamos es que el constructor de thread posee la posibilidad de recibir argumentos, y esto pasarlos a la funcion del argumento. Veamos el siguiente ejemplo:
#include <iostream>
#include <thread>
void sumar(int a, int b)
{
std::cout << "Total: " << a + b << std::endl;
}
int main()
{
std::thread hilo{sumar, 10, 15};
hilo.join();
return 0;
}
Primero definimos una funcion simple donde recibe dos valores y muestra la suma de los dos argumentos. En el main, creamos un thread donde pasamos la funcion junto a los dos argumentos para la funcion. El join se encarga de indicarle que finalice al main thread cuando termine hilo. Compilemos y veamos como es la salida::
$ ./threads
Total: 25
$
Es tan simple como eso, pero esta es la forma directa de pasar un argumento pero que sucede si necesitamos hacerlo por referencia? Veamos el siguiente ejemplo:
#include <iostream>
#include <thread>
class MiClase {};
void cambiar(MiClase& objeto)
{
std::cout << "Es solo un mensaje" << std::endl;
}
void puede_fallar()
{
MiClase objeto;
std::jthread hilo{cambiar, objeto};
}
int main()
{
return 0;
}
Primero definimos una clase vacia que solamente usaremos para el ejemplo. La primer funcion es para hacer cambios en un objeto de la clase anterior, y para ello lo recibiremos como referencia. En la siguiente funcion, bautizada como puede_fallar y ya veremos por que, creamos un objeto de la clase y luego creamos el thread para asignar la funcion cambiar y le pasamos el objeto anterior. Compilemos a nuestro codigo:
$ g++ --std=c++20 threads.cpp -o threads
In file included from threads.cpp:2:
/usr/include/c++/12/thread: In instantiation of ‘static std::thread std::jthread::_S_create(std::stop_source&, _Callable&&, _Args&& ...) [with _Callable = void (&)(MiClase&); _Args = {MiClase&}]’:
/usr/include/c++/12/thread:128:28: required from ‘std::jthread::jthread(_Callable&&, _Args&& ...) [with _Callable = void (&)(MiClase&); _Args = {MiClase&}; <template-parameter-1-3> = void]’
threads.cpp:14:35: required from here
/usr/include/c++/12/thread:235:27: error: static assertion failed: std::jthread arguments must be invocable after conversion to rvalues
235 | static_assert(is_invocable_v<decay_t<_Callable>,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
236 | decay_t<_Args>...>,
| ~~~~~~~~~~~~~~~~~~
/usr/include/c++/12/thread:235:27: note: ‘std::is_invocable_v<void (*)(MiClase&), MiClase>’ evaluates to false
$
Nos devolvio un error pero por que? Porque intenta llamar a cambiar con un valor de referencia. Debido a que el constructor de thread copia el valor recibido como argumento, y luego lo pasa al thread como una referencia, y esto lo hace fallar al momento de compilarlo porque no podemos pasar un valor de referencia a una funcion que espera un valor no constante. Para solucionarlo debemos modificar a puede_fallar de la siguiente manera:
void puede_fallar()
{
MiClase objeto;
std::jthread hilo{cambiar, std::ref(objeto)};
}
En este csao agregamos a la funcion ref para indicarle que ese argumento es de tipo referencia. Si lo compilan, ahora si lo hara. Debido a esto, cuando se trabaja con threads y se pasan argumentos se debe tener un poco mas de cuidado porque estas situaciones inesperadas pueden surgir. Pero no se preocupen porque esto se puede solucionar pero eso sera tema de un proximo post.
En resumen, hoy hemos visto como pasar argumentos a otro thread, de la manera mas simple, siendo valores directos, luego por referencia, la falla que puede surgir, asi como tambien solucionarlo. Espero les haya resultado 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.


Donation
It’s for maintenance of the site, thanks!
$1.50
