C ++ Rejoint Fil

C ++ Rejoint Fil
Les fils sont comme des sous-programmes à un programme. Et donc, ils couraient indépendamment à moins qu'ils ne soient rejoints. La jonction signifie qu'un fil fonctionnera jusqu'à ce qu'il atteigne un certain point, puis s'arrête et attend un autre fil pour terminer son exécution (jusqu'à sa fin); Avant de continuer sa propre exécution. Au point où le premier fil s'arrête, il y a une déclaration de jointure. Un fil peut appeler un autre fil. C'est le fil qui appelle, qui rejoint. Le fil appelé ne rejoint pas.

La rejoindre existe parce que les fils doivent communiquer entre eux. Une fois la modification. Ceci est une forme de synchronisation.

Cet article explique deux façons de rejoindre les fils. Il commence par une illustration de ce qu'est un fil.

Contenu de l'article

  • Fil
  • Rejoindre un fil
  • Future :: get ()
  • Conclusion

Fil

Considérez le programme suivant:

#inclure
Utilisation de Namespace Std;
void fn2 ()
couter << "function 2" << '\n';

void fn1 ()
couter << "function 1" << '\n';

int main()

/ * quelques instructions * /
retour 0;

fn1 (), fn2 () et main () sont des fonctions de niveau supérieur, bien que main () soit une fonction clé. Trois fils peuvent être obtenus à partir de ces trois fonctions de niveau supérieur. Le programme court très simple suivant est un fil naturel:

#inclure
Utilisation de Namespace Std;
int main()

/ * quelques instructions * /
retour 0;

La fonction principale () se comporte comme un fil. Il peut être considéré comme le fil principal. Il n'a pas besoin d'être encapsulé dans aucune instanciation de la classe de threads. Ainsi, le programme précédent avec des fonctions de niveau supérieur qui inclut la fonction principale () est toujours un thread. Le programme suivant montre comment les deux fonctions, fn1 () et fn2 () peuvent être converties en threads, mais sans aucune déclaration de jointure:

#inclure
#inclure
#inclure
Utilisation de Namespace Std;
String GloBl1 = String ("SO,");
String GloBl2 = String ("Que ce soit!");
void fn2 (String st2)
String GloBl = GloBl1 + ST2;
couter << globl << endl;

void fn1 (String st1)
globl1 = "Oui. "+ ST1;
Thread Thr2 (FN2, GloBl2);

int main()

Thread Thr1 (FN1, GloBl1);
/ * quelques instructions * /
retour 0;

Le programme commence par l'inclusion de la bibliothèque iOStream pour l'objet cout. Ensuite, la bibliothèque de threads est incluse. L'inclusion de la bibliothèque de threads est un must; Pour que le programmeur n'instanciera simplement un objet de thread de la classe de thread à l'aide d'une fonction de niveau supérieur, à l'exception de la fonction principale ().

Après cela, la bibliothèque de chaînes est incluse. Cette bibliothèque simplifie l'utilisation des littéraux de cordes. La première instruction du programme insiste sur le fait que tout nom utilisé provient de l'espace de noms standard C ++, sauf indication contraire.

Les deux déclarations suivantes déclarent deux objets de chaîne globale avec leurs littéraux. Les objets de chaîne sont appelés Globl1 et Globl2. Il y a la fonction fn1 () et la fonction fn2 (). Le nom de la fonction fn2 () sera utilisé comme l'un des arguments pour instancier le thread, thr2, de la classe de threads. Lorsqu'une fonction est instanciée de cette manière, la fonction est appelée; Et il exécute. Lorsque la fonction fn2 () est appelée, elle concaténe les littéraux de cordes de GloBl1 et GloBl2 pour avoir «donc, que ce soit!". Globl2 est l'argument de fn2 ().

Le nom de la fonction fn1 () est utilisé comme argument à l'instanciation du thread, thr1, de la classe de thread. Au cours de cette instanciation, fn1 () est appelé. Quand on l'appelle, il précède la chaîne, «donc, que ce soit!"Avec" Oui. ", pour avoir" Oui. Ainsi soit-il!", qui est la sortie de l'ensemble du programme de threads.

La fonction principale (), qui est le fil principal (), instancie le thread, thr1 de la classe de thread, avec les arguments FN1 et Globl1. Au cours de cette instanciation, fn1 () est appelé. La fonction, fn1 () est le thread effectif de l'objet, thr1. Lorsqu'une fonction est appelée, elle devrait fonctionner du début à sa fin.

Thr1, qui est effectivement fn1 (), instancie le fil, thr2, de la classe de thread, avec les arguments FN2 et Globl2. Au cours de cette instanciation, fn2 () est appelé. La fonction, fn2 () est le thread effectif de l'objet, thr2. Lorsqu'une fonction est appelée, elle devrait fonctionner du début à sa fin.

Si le lecteur utilise, le compilateur G ++, il peut tester ce programme de threads, pour la compilation C ++ 20, avec la commande suivante:

g ++ -std = c ++ 2a temp.cpp -lpthread -o temp

L'auteur l'a fait; dirigé le programme et a eu la production:

terminer appelé sans exception active
Avorté (noyau déversé)

Il est possible d'avoir une sortie erronée comme celle-ci, avec la sortie appropriée de "Oui. Ainsi soit-il!", interrompu à l'intérieur. Cependant, tout ce qui est encore inacceptable.

Le problème avec cette sortie erronée est que les threads n'ont pas été rejoints. Et donc, les fils fonctionnaient indépendamment, conduisant à la confusion. La solution consiste à rejoindre Thr1 au thread principal, et depuis Thr1 appelle Thr2, tout comme le fil principal appelle Thr1, Thr2 doit être joint à Thr1. Ceci est illustré dans la section suivante.

Rejoindre un fil

La syntaxe pour rejoindre un thread au fil d'appel est:

threadobj.rejoindre();

où join () est une fonction membre d'un objet thread. Cette expression doit être à l'intérieur du corps du fil d'appel. Cette expression doit être à l'intérieur du corps de la fonction d'appel, qui est un fil efficace.

Le programme suivant est le programme ci-dessus répété, mais avec le corps du fil principal qui rejoint Thr1, et le corps de Thr1 rejoignant Thr2:

#inclure
#inclure
#inclure
Utilisation de Namespace Std;
String GloBl1 = String ("SO,");
String GloBl2 = String ("Que ce soit!");
void fn2 (String st2)
String GloBl = GloBl1 + ST2;
couter << globl << endl;

void fn1 (String st1)
globl1 = "Oui. "+ ST1;
Thread Thr2 (FN2, GloBl2);
thr2.rejoindre();

int main()

Thread Thr1 (FN1, GloBl1);
thr1.rejoindre();
/ * quelques instructions * /
retour 0;

Notez les positions d'attente, où les instructions de jointure ont été insérées dans le programme. La sortie est:

"Oui. Ainsi soit-il!"

Sans les citations, comme prévu, propre et claire, sans ambiguïté ». Thr2 n'a besoin d'aucune déclaration de jointure dans son corps; il n'appelle aucun fil.

Ainsi, le corps du fil d'appel rejoint le fil appelé.

Future :: get ()

La bibliothèque standard C ++ a une sous-bibliothèque appelée Future. Cette sous-bibliothèque a une classe appelée Future. La bibliothèque a également une fonction appelée async (). La classe, Future, a une fonction membre appelée get (). En plus de son rôle principal, cette fonction a pour effet de rejoindre deux fonctions qui s'exécutent simultanément ou en parallèle. Les fonctions ne doivent pas être des threads.

La fonction async ()

Remarquez que les threads renvoient avant tout. Un thread est une fonction qui est sous contrôle. Certaines fonctions ne reviennent pas vides mais renvoient quelque chose. Donc, certains fils renvoient quelque chose.

La fonction async () peut prendre une fonction de niveau supérieur comme un argument et exécuter la fonction simultanément ou en parallèle avec la fonction d'appel. Dans ce cas, il n'y a pas de threads, juste une fonction d'appel et une fonction appelée fonction appelée un argument à la fonction async (). Une syntaxe simple pour la fonction asynchrone est:

Future FutoBj = async (fn, fnargs)

La fonction asynchrone renvoie un futur objet. Le premier argument ici, pour la fonction async, est le nom de la fonction de niveau supérieur. Il peut y avoir plus d'un argument après cela. Les autres arguments sont des arguments à la fonction de niveau supérieur.

Si la fonction de niveau supérieur renvoie une valeur, cette valeur sera un membre de l'objet futur. Et c'est une façon d'imiter un fil qui renvoie une valeur.

Future :: get ()

La fonction asynchrone renvoie un futur objet. Cet objet futur a la valeur de retour de la fonction qui est un argument à la fonction asynchrone. Afin d'obtenir cette valeur, la fonction de membre get () de l'objet futur doit être utilisée. Le scénario est:

Future FuTOBJ = async (fn, fnargs);
Type Futobj.obtenir();

Lorsque la fonction get () est appelée, le corps de la fonction d'appel attend (blocs), jusqu'à ce que la fonction asynchrone ait renvoyé sa valeur. Après cela, le reste des instructions sous l'instruction get () continue d'exécuter.

Le programme suivant est celui ci-dessus, où aucun fil n'a été créé officiellement, et à la place de la déclaration de jointure, la déclaration get () a été utilisée. La fonction async () a été utilisée pour simuler un fil. Les deux fonctions de niveau supérieur ont été réduites à une. Le programme est:

#inclure
#inclure
#inclure
Utilisation de Namespace Std;
String GloBl1 = String ("SO,");
String GloBl2 = String ("Que ce soit!");
String fn (String st1, String st2)
String Concat = ST1 + ST2;
Retour Concat;

int main()

Future FUT = Async (FN, GloBl1, GloBl2);
String ret = FUT.obtenir(); // main () attend ici
Résultat de la chaîne = "Oui. "+ ret;
couter << result << endl;
retour 0;

Notez que la future bibliothèque, au lieu de la bibliothèque de threads, a été incluse. La sortie est:

Oui. Ainsi soit-il!

Conclusion

Lorsqu'une déclaration de jointure est concernée, deux fonctions de haut niveau sont impliquées. L'un est la fonction d'appel, et l'autre est la fonction appelée. Dans le corps de la fonction d'appel est l'instruction JOIN. Ces deux fonctions peuvent être encapsulées chacune dans un fil. La fonction de membre join () du thread appelé est dans le corps du fil d'appel. Lorsque la fonction join () est appelée, le thread d'appel attend à ce point (blocs) jusqu'à ce que le thread appelé se termine; Avant de continuer à fonctionner.

L'utilisation de threads peut être évitée en utilisant la fonction async () dans la future bibliothèque. Cette fonction prend une fonction de niveau supérieur comme un argument et renvoie un futur objet, qui contient la valeur renvoyée de la fonction d'argument à la fonction async (). Afin d'obtenir la valeur de retour de la fonction comme un argument, la fonction de membre get () de l'objet futur doit être utilisée. Lorsque la fonction de membre get () est appelée, le corps de la fonction d'appel attend à ce point (blocs) jusqu'à ce que la fonction appelée se termine; Avant de continuer à fonctionner.