Unirse existe porque los hilos deben comunicarse entre sí. Una vez que se ha llevado a cabo el cambio, el hilo llamado puede cambiar el valor de una variable global, a la que el hilo de llamadas debe acceder. Esta es una forma de sincronización.
Este artículo explica dos formas de unir los hilos. Comienza con una ilustración de lo que es un hilo.
Contenido del artículo
Hilo
Considere el siguiente programa:
#incluir
usando el espacio de nombres STD;
void fn2 ()
cout << "function 2" << '\n';
vacío fn1 ()
cout << "function 1" << '\n';
int main ()
/ * Algunas declaraciones */
regresar 0;
fn1 (), fn2 () y main () son funciones de nivel superior, aunque main () es una función clave. Se pueden obtener tres hilos de estas tres funciones de nivel superior. El siguiente programa corto muy simple es un hilo natural:
#incluir
usando el espacio de nombres STD;
int main ()
/ * Algunas declaraciones */
regresar 0;
La función principal () se comporta como un hilo. Se puede considerar como el hilo principal. No necesita ser encapsulado en ninguna instancia de la clase de hilo. Entonces, el programa anterior con funciones de nivel superior que incluye la función Main () sigue siendo un hilo. El siguiente programa muestra cómo las dos funciones, FN1 () y Fn2 () se pueden convertir en hilos, pero sin ninguna declaración de unión:
#incluir
#incluir
#incluir
usando el espacio de nombres STD;
string globl1 = string ("So,");
string globl2 = string ("¿Ya sea!");
void fn2 (string st2)
String Globl = GloBl1 + ST2;
cout << globl << endl;
void fn1 (string st1)
GLOBL1 = "SÍ. " + ST1;
hilo Thr2 (FN2, GLOBL2);
int main ()
hilo Thr1 (FN1, GloBl1);
/ * Algunas declaraciones */
regresar 0;
El programa comienza con la inclusión de la biblioteca IOSTream para el objeto Cout. Entonces se incluye la biblioteca de hilos. La inclusión de la biblioteca de subprocesos es imprescindible; para que el programador simplemente instanciará un objeto de subproceso de la clase de subprocesos utilizando una función de nivel superior, excepto la función main ().
Después de eso, se incluye la biblioteca de cadenas. Esta biblioteca simplifica el uso de literales de cadenas. La primera declaración en el programa insiste en que cualquier nombre utilizado es del espacio de nombres estándar de C ++ a menos que se indique lo contrario.
Las siguientes dos declaraciones declaran dos objetos globales de cadena con sus literales. Los objetos de cadena se llaman GLOBL1 y GLOBL2. Existe la función fn1 () y la función fn2 (). El nombre de la función fn2 () se utilizará como uno de los argumentos para instanciar el hilo, Thr2, de la clase de hilo. Cuando una función se instancia de esta manera, se llama la función; y se ejecuta. Cuando se llama a la función fn2 (), concatena los literales de cadena de GloBl1 y GloBl2 para tener "así que, que así sea!". GloBl2 es el argumento de fn2 ().
El nombre de la función fn1 () se usa como un argumento para la instancia del hilo, Thr1, de la clase de hilo. Durante esta instanciación, se llama fn1 (). Cuando se llama, precede a la cadena: “Entonces,!"Con" Sí. ", para tener" si. Que así sea!", que es la salida para todo el programa de subprocesos.
La función Main (), que es el hilo principal (), instancia el hilo, Thr1 de la clase de hilo, con los argumentos FN1 y GloBl1. Durante esta instanciación, se llama fn1 (). La función, fn1 () es el hilo efectivo para el objeto, Thr1. Cuando se llama a una función, debe correr desde el principio hasta su fin.
Thr1, que es efectivamente fn1 (), instancia el hilo, Thr2, de la clase de hilo, con los argumentos FN2 y GloBl2. Durante esta instancia, se llama fn2 (). La función, fn2 () es el hilo efectivo para el objeto, thr2. Cuando se llama a una función, debe correr desde el principio hasta su fin.
Si el lector está usando, el compilador G ++, puede probar este programa de hilos, para la compilación C ++ 20, con el siguiente comando:
g ++ -std = C ++ 2a temperatura.CPP -LPTHREAD -O TEMP
El autor hizo esto; ejecutó el programa y tuvo la salida:
Terminar llamado sin una excepción activa
Abortado (núcleo arrojado)
Es posible tener una salida errónea como esta, con la salida adecuada de "Sí. Que así sea!", ranurado dentro. Sin embargo, todo eso es inaceptable.
El problema con esta salida errónea es que los hilos no se unieron. Y así, los hilos funcionaban de forma independiente, lo que lleva a la confusión. La solución es unir a Thr1 al hilo principal, y dado que Thr1 llama a Thr2, al igual que el hilo principal llama a Thr1, THR2 debe unirse a Thr1. Esto se ilustra en la siguiente sección.
Uniéndose a un hilo
La sintaxis para unir un hilo en el hilo de llamadas es:
threadobj.unirse();
donde unir () es una función miembro de un objeto de hilo. Esta expresión tiene que estar dentro del cuerpo del hilo de llamadas. Esta expresión debe estar dentro del cuerpo de la función de llamadas, que es un hilo efectivo.
El siguiente programa es el programa anterior repetido, pero con el cuerpo del hilo principal que se une a Thr1 y el cuerpo de Thr1 se une a Thr2:
#incluir
#incluir
#incluir
usando el espacio de nombres STD;
string globl1 = string ("So,");
string globl2 = string ("¿Ya sea!");
void fn2 (string st2)
String Globl = GloBl1 + ST2;
cout << globl << endl;
void fn1 (string st1)
GLOBL1 = "SÍ. " + ST1;
hilo Thr2 (FN2, GLOBL2);
thr2.unirse();
int main ()
hilo Thr1 (FN1, GloBl1);
thr1.unirse();
/ * Algunas declaraciones */
regresar 0;
Tenga en cuenta las posiciones de espera, donde se han insertado las declaraciones de unión en el programa. La salida es:
"Sí. Que así sea!"
Sin las citas, como se esperaba, limpia y clara, inequívoca ". Thr2 no necesita ninguna declaración de unión en su cuerpo; no llama a ningún hilo.
Entonces, el cuerpo del hilo de llamada se une al hilo llamado.
Future :: get ()
La biblioteca estándar C ++ tiene un sub-bibliotecario llamado futuro. Este sub-bibliotecario tiene una clase llamada Future. La biblioteca también tiene una función llamada async (). La clase, Future, tiene una función miembro llamada get (). Además de su papel principal, esta función tiene el efecto de unir dos funciones que se ejecutan simultáneamente o en paralelo. Las funciones no tienen que ser hilos.
La función async ()
Observe que los hilos sobre todo regresan nulo. Un hilo es una función que está bajo control. Algunas funciones no devuelven nulo pero devuelven algo. Entonces, algunos hilos devuelven algo.
La función async () puede tomar una función de nivel superior como argumento y ejecutar la función simultáneamente o en paralelo con la función de llamada. En este caso, no hay hilos, solo una función de llamadas y una función llamada llamada un argumento a la función async (). Una sintaxis simple para la función async es:
FUTUS FUTOBJ = Async (FN, FNARGS)
La función async devuelve un objeto futuro. El primer argumento aquí, para la función async, es el nombre de la función de nivel superior. Puede haber más de un argumento después de esto. El resto de los argumentos son argumentos a la función de nivel superior.
Si la función de nivel superior devuelve un valor, entonces ese valor será un miembro del objeto futuro. Y esta es una forma de imitar un hilo que devuelve un valor.
Future :: get ()
La función async devuelve un objeto futuro. Este objeto futuro tiene el valor de retorno de la función que es un argumento para la función async. Para obtener este valor, la función miembro get () del objeto futuro debe usarse. El escenario es:
futuro futobj = async (fn, fnargs);
Escriba FUTOBJ.conseguir();
Cuando se llama a la función get (), el cuerpo de la función de llamada espera (bloques), hasta que la función async ha devuelto su valor. Después de eso, el resto de las declaraciones debajo de la instrucción get () continúan ejecutándose.
El siguiente programa es el anterior, donde no se ha creado ningún hilo oficialmente, y en lugar de la declaración de unión, la declaración get () se ha utilizado. La función async () se ha utilizado para simular un hilo. Las dos funciones de nivel superior se han reducido a una. El programa es:
#incluir
#incluir
#incluir
usando el espacio de nombres STD;
string globl1 = string ("So,");
string globl2 = string ("¿Ya sea!");
String fn (string st1, string st2)
cadena concat = ST1 + ST2;
devolver concat;
int main ()
futuro fut = async (fn, globl1, globl2);
cadena ret = fut.conseguir(); // main () espera aquí
Resultado de cadena = "Sí. " + ret;
cout << result << endl;
regresar 0;
Tenga en cuenta que la biblioteca futura, en lugar de la biblioteca de subprocesos, se ha incluido. La salida es:
Sí. Que así sea!
Conclusión
Cuando se trata de una declaración de unión, están involucradas dos funciones de nivel superior. Una es la función de llamada, y la otra es la función llamada. En el cuerpo de la función de llamadas está la declaración de unión. Estas dos funciones pueden estar encapsuladas en un hilo. La función miembro de unión () del hilo llamado está en el cuerpo del hilo de llamada. Cuando se llama a la función Join (), el hilo de llamada espera en ese punto (bloquea) hasta que el hilo llamado se complete; Antes de que continúe operando.
El uso de hilos se puede evitar utilizando la función async () en la biblioteca futura. Esta función toma una función de nivel superior como argumento y devuelve un objeto futuro, que contiene el valor devuelto de la función de argumento a la función async (). Para obtener el valor de retorno de la función como argumento, la función miembro get () del objeto futuro debe usarse. Cuando se llama a la función de miembro get (), el cuerpo de la función de llamada espera en ese punto (bloquea) hasta que la función llamada se complete; Antes de que continúe operando.