Multithreading Unity et coroutines

1. Unity prend-il en charge le multithreading ?

Unity prend en charge l'utilisation du multithreading. Vous pouvez utiliser la classe Thread de C# pour créer et gérer des threads. Il vous suffit d'introduire cette classe :

Mais il convient de noter que dans Unity, seul le thread principal (également appelé thread de rendu) peut accéder aux objets Unity, tels que GameObject, Transform, etc. Si vous accédez à ces objets dans d'autres threads, cela conduira à des résultats imprévisibles.

Par conséquent, lors de l'utilisation du multithreading, certaines règles doivent être suivies :

  1. N'accédez pas aux objets Unity dans les threads non principaux ;

  2. Ne modifiez pas le même objet ou la même variable dans plusieurs threads en même temps, sinon cela pourrait provoquer des conditions de concurrence ;

  3. N'abusez pas du multi-threading, car la commutation de thread entraînera une surcharge supplémentaire, et le multi-threading peut augmenter la complexité du code et la difficulté de débogage ;

  4. N'oubliez pas de fermer le multithreading dans Unity après utilisation, sinon il continuera à fonctionner après le débogage, jusqu'à ce que vous fermiez Unity ou que vous changiez le code de script correspondant.

Exemple: 

 Bien que le thread nouvellement ouvert ne puisse pas accéder aux objets dans Unity, le multi-threading peut effectuer certains calculs logiques complexes : comme l'algorithme A*, le calcul de grille, des calculs complexes peuvent bloquer le thread principal, nous utilisons donc des threads secondaires pour calculer. Oubliez ça, le fil principal l'appellera à nouveau.

Exemple:

 

 

 2. Qu'est-ce qu'une coroutine ?

Le nom complet de la coroutine est coroutine. Contrairement au multithreading, ce n'est pas un thread et est attaché au thread principal de Unity.

  • L'ouverture d'un nouveau thread est un pipeline indépendant, qui est exécuté en parallèle avec le thread principal ;
  • L'ouverture d'une nouvelle coroutine est lancée au-dessus du thread d'origine, et la logique est exécutée en temps partagé et étape par étape ;

Effet principal:

        Les coroutines conviennent aux opérations qui doivent être effectuées par étapes, telles que les animations, l'exécution différée, etc. Les coroutines permettent à un programme de s'exécuter à nouveau après un certain temps sans bloquer le thread principal. Cela peut éviter la surcharge causée par le changement de thread et améliorer les performances du programme.

        C'est-à-dire que la coroutine consiste à effectuer des opérations chronophages qui peuvent provoquer le blocage du thread principal dans le temps et pas à pas.

Principaux scénarios applicables :

        Chargez des fichiers de manière asynchrone, téléchargez des fichiers de manière asynchrone, chargez des scènes de manière asynchrone et évitez les décalages lors de la création de lots.


        Les coroutines peuvent diviser une fonction en plusieurs parties, faire une pause après l'exécution de chaque partie et attendre le prochain réveil pour continuer l'exécution. Si elle n'est pas réveillée, la coroutine sera suspendue. Cette méthode peut contrôler efficacement le flux d'exécution du programme, de sorte que nous pouvons contrôler la logique de fonctionnement du programme de manière plus flexible.

3. L'utilisation de coroutines

Tout d'abord, pour utiliser des coroutines, la classe doit hériter de MonoBehavior ;

Dans Unity, nous pouvons utiliser la classe Coroutine pour créer des coroutines. Habituellement, nous définissons une coroutine en tant que fonction (à l'aide du mot-clé IEnumerator), puis nous la démarrons via la méthode StartCoroutine.

Exemple:

        Dans cet exemple, nous définissons une fonction coroutine nommée MyCoroutine, qui utilise l'instruction yield pour faire une pause de 1 seconde et 2 secondes respectivement, et génère un message de débogage après chaque pause. Ensuite, dans la fonction Start, nous démarrons la coroutine via la méthode StartCoroutine.

        Il convient de noter que la fonction coroutine doit renvoyer le type IEnumerator, et non le type void. L'instruction yield est utilisée dans la fonction coroutine pour suspendre l'exécution et renvoyer un objet indiquant au système coroutine quand continuer l'exécution.


        Les coroutines peuvent contrôler leur comportement en passant des paramètres. Nous pouvons prendre des arguments comme arguments de la fonction coroutine et lui passer lors du démarrage de la coroutine.

 Exemple:

        Dans cet exemple, nous définissons une fonction coroutine appelée MyCoroutine qui prend deux paramètres : une chaîne et un flottant. Ensuite, dans la fonction Start, nous avons démarré la coroutine via la méthode StartCoroutine, et nous lui avons respectivement transmis "Hello" et 3 comme paramètres.

        Il convient de noter qu'à l'intérieur de la fonction coroutine, nous pouvons utiliser les paramètres transmis comme des fonctions ordinaires. Cependant, en dehors de la fonction coroutine, nous ne pouvons pas accéder directement aux variables à l'intérieur de la coroutine. Si vous avez besoin de partager des données entre des coroutines, pensez à utiliser des variables statiques ou d'autres mécanismes thread-safe.

Différents rendements

  1. yield return null : Suspendez la coroutine pendant une image, puis continuez à exécuter l'image suivante, entre Update et LateUpdate ;

  2. yield return new WaitForSeconds(float seconds): suspend la coroutine pendant une durée spécifiée et continue l'exécution entre Update et LateUpdate ;

  3. yield return new WaitForEndOfFrame() : Suspendez la coroutine jusqu'à ce que l'image actuelle soit rendue (caméra et interface graphique) et continuez à exécuter, après le traitement lié au rendu après LateUpdate ;

  4. yield return new WaitForFixedUpdate() : suspend la coroutine jusqu'à la prochaine FixedUpdate et continue à s'exécuter, après FixUpdate et les fonctions liées à la détection de collision ;

  5. yield return StartCoroutine(coroutine) : suspend la coroutine actuelle et démarre une nouvelle coroutine, et continue à exécuter la coroutine actuelle jusqu'à ce que la nouvelle coroutine soit exécutée.

  6. yield break : termine l'exécution de la coroutine en cours.

 4. Les coroutines sont affectées par l'inactivation et la destruction d'objets et de composants

Quand on démarre une coroutine, si

  • Les composants et objets qui montent ce script sont détruits et la coroutine ne sera pas exécutée ;
  • La coroutine de désactivation de l'objet n'est pas exécutée ;
  • Exécution de coroutine de désactivation de composant.

5. Le principe de la coroutine 

L'essence des coroutines :

        L'essence des coroutines est implémentée à l'aide d'itérateurs. En C#, un itérateur est un objet spécial qui renvoie les éléments d'une collection séquentiellement dans une boucle. Dans la coroutine, l'instruction yield return renvoie également un objet itérateur. Lorsque la coroutine exécute l'instruction yield return, elle transmet le droit d'exécution à l'appelant et renvoie un objet itérateur, en attendant que l'appelant appelle à nouveau la coroutine. Continuez à exécuter. De cette manière, la fonction de suspension et de poursuite de l'exécution de la coroutine est réalisée.

        Ainsi, appeler une coroutine dans un script équivaut à mettre une fonction coroutine (itérateur) dans le planificateur de coroutine de Unity (fonction StartCoroutine()) pour nous aider à gérer et à exécuter.Les règles spécifiques derrière le retour de rendement sont également définies par Unity certaines règles.

        On simule manuellement la gestion de la coroutine en obtenant l'objet IEnumerator ;

Définissez une fonction coroutine qui ne peut pas être appelée seule :

 Obtenez cet objet coroutine :

      La méthode interne de IEnumerator, les propriétés sont les suivantes :

Exécutez MoveNext(), la console affichera "test 1" et une ligne d'instruction est exécutée ;

Exécutez Reset(), la console affichera "1", qui imprime la valeur de retour de yield return ;

Si Current est imprimé, la console affichera la valeur du paramètre de yield return ;

Par exemple, de cette manière, Reset affichera "123 Vector3" et Current affichera "123 (1, 2, 3)" ;

 Un autre problème vient, nous pouvons utiliser MoveNext() et Current pour exécuter la fonction coroutine étape par étape. Mais que se passe-t-il s'il y a n retours de rendement dans la fonction coroutine ? Écrivez n MoveNext() et Current ?

 Remarque : MoveNext() renvoie une valeur booléenne, renvoie true lorsqu'il y a encore du contenu exécutable dans la fonction coroutine et ne renvoie pas flase.

Utilisez while output pour exécuter toutes les valeurs de la fonction coroutine :

Résumer:

        L'essence de la coroutine est d'utiliser la fonctionnalité "d'exécution pas à pas" de la fonction d'itération C # et un ensemble de règles de fonction d'exécution en temps partagé implémentées par la logique de planification de la coroutine.

おすすめ

転載: blog.csdn.net/HimaRe1/article/details/131096031