Concurrence de rouille

Concurrence de rouille
La concurrence se réfère à une fonctionnalité qui permet aux parties autonomes d'un programme de s'exécuter parallèlement à d'autres sections du code. La concurrence permet à différentes parties d'un programme de s'exécuter simultanément, en améliorant les performances.

Promettons-nous dans les bois de la programmation de concurrence dans le langage de programmation de rouille. Gardez à l'esprit que cet article n'est pas conçu pour être un guide complet de la programmation de concurrence. Il ne sert que de base pour l'expansion et la création d'applications plus complexes.

Processus et threads

Lorsque nous écrivons un programme normal et l'exécutons sur un système cible, le système d'exploitation hôte exécute le code dans un processus. Un processus fait référence à une unité d'un exécutable spécifié.

Cependant, dans les systèmes et applications modernes, vous avez des parties du même code exécutées simultanément à l'aide de threads.

Dans la plupart des cas, vous entendrez souvent le terme multi-threading utilisé lorsque la concurrence se produit. En effet.

Prenons un programme de base pour illustrer le fonctionnement d'un programme normal et comment utiliser la concurrence pour l'améliorer.

Considérez un programme avec deux boucles comme indiqué:

Utilisez Std :: Thread;
Utilisez Std :: Time :: durée;
fn main ()
pour je dans 0… = 5
println!("", je);
// Dormez pendant 1000 ms
Thread :: Sleep (Durée :: From_millis (1000));

pour je dans 0… = 5
println!("", je);
Thread :: Sleep (Durée :: From_millis (1000));

Dans l'exemple de code ci-dessus, nous avons deux boucles qui itèrent de 0 à 5. Cependant, dans chaque itération, nous dormons pendant 1000 millisecondes.

La méthode du thread :: Sleep nous permet de mettre un fil spécifique à dormir pendant la durée spécifiée.

Si vous exécutez le code ci-dessus, vous remarquez que la première boucle attend la deuxième boucle avant de pouvoir commencer à fonctionner.

C'est parce que les deux boucles sont sur un seul fil.

Si nous voulons que les deux boucles fonctionnent simultanément, nous devons les mettre dans différents fils.

Rust Créer du fil

Nous pouvons créer de nouveaux threads à l'aide du module de thread. Il fait partie de la bibliothèque standard et nous fournit une suite d'outils et de fonctions pour travailler avec des threads.

Nous pouvons l'importer en utilisant l'instruction:

Utilisez Std :: Thread;

Nous avons également besoin du module de durée à partir du fil de temps. Nous pouvons l'importer comme:

Utilisez Std :: Time :: Durée

Pour créer un nouveau fil dans Rust, utilisez la méthode du fil :: Spawn. Cette méthode prend une fermeture comme argument.

La fermeture, dans ce cas, définit le code à exécuter à l'intérieur du fil.

La syntaxe est comme indiqué ci-dessous:

Thread :: Spawn (|| fermeture)

Laissez affiner le code précédent et mettons chaque construction dans un fil séparé. L'exemple de code est comme indiqué:

Utilisez Std :: Thread;
Utilisez Std :: Time :: durée;
fn main ()
// Créer un nouveau fil
Std :: Thread :: Spawn (Move ||
pour je dans 0… = 5
println!("", je);
Thread :: Sleep (Durée :: From_millis (1000));

);
pour je dans 0… = 5
println!("", je);
Thread :: Sleep (Durée :: From_millis (1000));

Dans l'exemple de programme ci-dessus, nous créons un nouveau thread à l'aide de la fonction Thread :: Spawn et passons la première boucle comme fermeture.

Dans le fil principal, nous exécutons la deuxième boucle. Cela permet aux deux boucles d'exécuter simultanément. Le code ci-dessus doit renvoyer la sortie comme:

0
0
1
1
2
2
3
3
4
4
5
5

Que se passe-t-il si le thread principal sort avant la fin du fil «intérieur»? Un exemple est comme indiqué ci-dessous:

Utilisez Std :: Thread;
Utilisez Std :: Time :: durée;
fn main ()
// Fil intérieur
Std :: Thread :: Spawn (Move ||
pour je dans 0… = 5
println!("", je);
Thread :: Sleep (Durée :: From_millis (1000));

);
// principal
pour je dans 0… = 5
println!("", je);
thread :: sleep (durée :: from_millis (2));

Dans cet exemple, le fil principal prend moins de temps pour dormir et se terminera donc plus rapidement avant la fin du fil intérieur.

Dans un tel cas, le fil intérieur ne fonctionnera que pendant que le thread principal s'exécute. Le code ci-dessus renvoie une sortie incomplète comme:

0
0
1
2
3
4
5

C'est parce que le fil «intérieur» est terminé avant la fin.

Rust Rewles Poignées

Nous avons vu comment un thread se comporte si le thread principal sort avant de terminer. Nous pouvons rejoindre les deux poignées pour résoudre un tel cas et laisser l'autre fil en attendre un autre.

Les poignées d'adhésion permettra au fil principal d'attendre les autres threads avant la fin.

Pour joindre des poignées, nous utilisons la méthode de jointure comme indiqué dans la syntaxe ci-dessous:

Let handle_name = thread :: spawn (fermeture);
handle_name.rejoindre().déballer();

Laissez-nous redéfinir notre exemple de boucle, où le thread principal sort tôt.

Utilisez Std :: Thread;
Utilisez Std :: Time :: durée;
fn main ()
Let Handle = Std :: Thread :: Spawn (Move ||
pour je dans 0… = 5
println!("", je);
Thread :: Sleep (Durée :: From_millis (1000));

);
pour je dans 0… = 5
println!("", je);
thread :: sleep (durée :: from_millis (2));

// Rejoignez la poignée
gérer.rejoindre().déballer();

Dans l'exemple de code ci-dessus, nous créons une variable de manche qui contient le thread. Nous rejoignons ensuite le thread à l'aide de la méthode join ().

La méthode de déballage nous permet de gérer les erreurs.

Étant donné que le fil principal dort pendant une période plus courte, elle doit être terminée avant le fil «intérieur». Cependant, il devrait attendre que l'autre thread quitte en raison de la méthode de jointure.

La sortie est comme indiqué:

0
0
1
2
3
4
5
1
2
3
4
5

Notez que le thread principal produit toutes les valeurs en courte durée et attend que l'autre fil termine.

Ferme de déplacement du fil de rouille

Vous avez peut-être remarqué le mot-clé Move à l'intérieur de la fermeture du fil dans notre exemple précédent. La fermeture de déménagement est utilisée avec la méthode de thread :: Spawn pour permettre le partage de données entre les threads.

En utilisant le mot-clé Move, nous pouvons permettre à un thread de transférer la propriété des valeurs vers un autre thread.

Prenez un exemple de programme ci-dessous:

Utilisez Std :: Thread;
Utilisez Std :: Time :: durée;
fn main ()
Soit arr = [1,2,3,4,5];
Let Handle = Std :: Thread :: Spawn (||
Pour je à Arr.iter ()
println!("", je);
Thread :: Sleep (Durée :: From_millis (100));

);
gérer.rejoindre().déballer();

Dans le code ci-dessus, nous déclarons un tableau appelé ARR dans le fil principal. Nous engendrez ensuite un nouveau fil sans la fermeture de déménagement.

Remarque: Étant donné que nous essayons d'accéder au tableau ARR et de le faire partie de l'environnement de fermeture, la compilation échouera car elle n'est pas disponible dans ce fil.

Nous pouvons utiliser le mot-clé Move pour forcer la fermeture du fil pour s'approprier le tableau.

Nous pouvons corriger le code ci-dessus en ajoutant la fermeture de déménagement comme indiqué:

Utilisez Std :: Thread;
Utilisez Std :: Time :: durée;
fn main ()
Soit arr = [1,2,3,4,5];
Let Handle = Std :: Thread :: Spawn (Move ||
Pour je à Arr.iter ()
println!("", je);
Thread :: Sleep (Durée :: From_millis (100));

);
gérer.rejoindre().déballer();

Cela permet au fil de s'approprier le tableau et de l'idéter. Cela devrait revenir:

1
2
3
4
5

Conclusion

C'était les principes fondamentaux de la programmation simultanée dans le langage de programmation Rust. Bien que cet article sert de base concrète à la concurrence de la rouille, il ne couvre pas les concepts avancés. Vous pouvez vérifier la documentation pour plus de détails.