Table des matières du kit Noyau | Index du kit du Noyau |
Un Thread est un processus synchrone qui exécute une série d'instructions de programmation. Une Team est un groupe de threads qui constitue un programme ou application.
Chaque application a au moins un thread : lorsque vous lancez une application, un thread initial—le thread principal (main thread)—est automatiquement créé (ou engendré (spawned)) et commandé de démarrer. Le thread principal exécute la fonction omniprésente main(), naviguant à travers les fonctions qui sont appelées depuis main(), et est automatiquement supprimé (ou tué (killed)) lorsque main() se termine.
BeOS est multi-threadé : depuis le thread principal vous pouvez engendrer et lancer des threads additionnels; depuis chacun de ces threads vous pouvez engendrer et lancer d'autres threads, et ainsi de suite. Tous les threads de toutes les applications (Teams) s'exécutent en parallèle et de manière asynchrone entre eux.
Les threads sont indépendants les uns des autres. En particulier, un thread donné ne possède pas les threads qu'il a engendré. Par exemple, si le thread A engendre le thread B, et le thread A meurt (peu importe la raison), le thread B continuera son exécution. (Mais avant que vous partiez avec l'idée de jouer à saute-moutons avec les threads, vous devriez prendre note de l'avertissement dans "Mort et le Thread Principal".)
|
Bien que les threads soient independants, ils aboutissent dans des groupes appellés teams. Une team consiste d'un thread principal et tous les threads qui "descendent" de lui (qui sont engendrés par le thread principal directement, ou par tous threads qui ont été engendrés par le thread principal, et ainsi de suite). Vu d'un niveau plus haut, une team est le group de threads qui ont été créés par une seule application. Vous ne pouvez pas "transférer" de threads d'une team à une autre. La team est affectée lorsque le thread est engendré; elle reste la même durant toute la vie du thread.
Tous les threads d'une team particulière partage le même espace d'adressage : les variables globales qui sont déclarées par un thread seront visibles par tous les autres threads de cette team.
Vous engendrez un thread en appellant la fonction spawn_thread(). La fonction affecte et retourne un thread_id unique à tout le système que vous utilisez pour identifier le nouveau thread dans des appels futurs aux fonctions de threads. Les thread_id valides sont des entiers positifs; vous pouvez vérifier le succès ainsi :
thread_id my_thread = spawn_thread(...); if ((my_thread) < B_OK) /* échec */ else /* succès */
Les arguments de spawn_thread(), qui sont examinés dans toute cette description, fournissent des informations comme que doit faire le thread, l'urgence de son fonctionnement, etc.
Un concept voisin de la génération d'un thread est le chargement d'un exécutable (ou chargement d'une image applicative). Cela s'effectue en appelant la fonction load_image() . Charger une image engendre un programme séparé, identifié par un fichier, qui sera lancé par le système. Pour plus d'information sur la fonction load_image(), consultez Images.
Engendrer un thread n'est pas suffisant pour le faire fonctionner. Pour dire à un thread de démarrer, vous devez passer son thread_id à soit la fonction resume_thread() soit la fonction wait_for_thread() :
De ces deux fonctions, resume_thread() est le moyen le plus commun pour démarrer un thread créé par spawn_thread(). wait_for_thread() est typiquement utilisée pour démarrer un thread créé par load_image().
Lorsque vous appellez spawn_thread(), vous devez désigner la fonction du thread du nouveau thread. C'est une fonction C globale (ou une methode C++ statique) que le nouveau thread exécutera lorsqu'on lui demandera de démarrer. La fonction du thread, définie par thread_func, prend un seul argument (void *) et retourne un code d'erreur de type int32. Lorsque la fonction du thread se termine, le thread est automatiquement tué.
Vous passez une fonction de thread comme premier argument à spawn_thread(). Par exemple, ici nous engendrons un thread qui utilise une fonction appelée lister() comme sa fonction de thread. Le dernier argument de spawn_thread() est transmis à la fonction du thread :
int32 lister(void *data) { /* Typons l'argument. */ BList *listObj = (BList *)data; ... } int32 main() { BList *listObj = new BList(); thread_id my_thread; my_thread = spawn_thread(lister, ..., (void *)listObj); resume_thread(my_thread); ... }
Consultez Passer des données à un thread pour d'autres méthodes de transmission de données à un thread.
Un thread peut recevoir un nom que vous spécifiez par le second argument de spawn_thread(). Le nom peut être long d'au plus 32 caractères (comme indiqué par la constante B_OS_NAME_LENGTH) et n'a pas besoin d'être unique—plusieurs threads peuvent avoir le même nom.
Vous pouvez chercher un thread d'après son nom en passant le nom à la fonction find_thread(); la fonction retourne le thread_id du thread portant ce nom. Si deux threads ou plus portent ce même nom, la fonction find_thread() retourne le premier qu'elle trouve.
Vous pouvez obtenir le thread_id du thread appelant en passant NULL to find_thread() :
thread_id this_thread = find_thread(NULL);
Pour obtenir le nom d'un thread, vous devez regarder dans la structure thread_info du thread. Cette structure est détailée dans la description de la fonction get_thread_info().
Pas satisfait du nom d'un thread ? Utilisez la fonction rename_thread() pour le changer. Dupez vos amis.
Dans un environnement multi-threadé, la ou les CPUs doivent diviser leur attention entre les threads candidats, exécutant quelques instructions d'un thread, puis quelques unes d'un autre thread, et ainsi de suite. Mais cette division n'est pas toujours égale : vous pouvez affecter une plus grande ou plus petite priorité à un thread et ainsi le déclarer comme plus ou moins important que les autres threads.
Vous affectez la priorité (un entier) d'un thread par le troisième argument à spawn_thread(). Il y a deux catégories de priorités : "temps partagé" et "temps-réel".
Le kit du Kernet définit sept constantes de priorité (voir Valeur de priorité de thread pour la liste). Bien que vous pouvez utiliser n'importe quelle autre valeur intermédiare comme argument de priorité de spawn_thread(), il est conseillé de rester à celles-ci.
De plus, vous pouvez appeller la fonction suggest_thread_priority() pour laisser le kit du Noyau proposer une bonne priorité pour votre thread. Cette fonction demande quelques informations sur la préemption du thread et ses besoins CPU, et retourne une valeur de priorité raisonable à utiliser pour engendrer le thread.
Parfois vous voudrez qu'un thread particulier s'arrête à un point précis jusqu'à ce qu'un autre thread (connu) termine sa tâche. Voici trois moyens d'effectuer ce type de synchronisation :
Il y a quatre moyens de contrôler un thread pendant son exécution :
Comme mentionné plus haut, le contrôle imposé à un thread en particulier n'impacte pas les éventuels "fils" engendrés par ce thread. Cependant, la mort du thread principal d'une application peut affecter les autres threads :
|
Il est possible de créer une application dans laquelle le thread principal préparerait un ou plusieurs threads, les lancerait, et ensuite décèderait. Mais de telles applications sont rares. En général, vous devez essayer de garder votre thread principal jusqu'à ce que tous les autres threads de la team soit morts.
Chaque thread a un cache message. Vous pouvez écrire dans le cache message d'un thread via la fonction send_data(). Le thread peut lire votre message (une combinaison d'un entier et d'un buffer) via receive_data(). Le cache ne contient qu'un seul message à la fois; s'il y a déjà un message dans le cache, send_data bloquera. A l'inverse, s'il n'y a aucun message dans le cache, receive_data() bloquera.
Vous pouvez aussi passer des données à un thread par un port. De profondeur arbitraire, les ports sont plus flexibles que le cache message. Consultez Ports pour les détails.
Table des matières du kit Noyau | Index du kit du Noyau |