Sur cette page

Qt TaskTree C++ Classes

Contient une bibliothèque d'arbres de tâches à usage général. Plus d'informations...

Ce module est en cours de développement et peut être modifié.

Ce module a été introduit dans Qt 6.11.

Espaces de noms

QtTaskTree

Contient toutes les classes et fonctions globales du module TaskTree.

Classes

QtTaskTree::Do

Un élément "body" utilisé dans les constructions "For" et "When".

QtTaskTree::Else

Élément "else" utilisé dans les expressions conditionnelles

QtTaskTree::ElseIf

Élément "else if" utilisé dans les expressions conditionnelles

QtTaskTree::ExecutableItem

Classe de base pour les éléments de tâche exécutables

QtTaskTree::ExecutionMode

Élément de groupe décrivant le mode d'exécution

QtTaskTree::For

Élément de boucle for

QtTaskTree::Forever

Boucle infinie de sous-tâches

QtTaskTree::ForeverIterator

Itérateur infini à utiliser dans l'élément For

QtTaskTree::Group

Représente l'élément de base pour composer des recettes déclaratives décrivant comment exécuter et gérer un arbre imbriqué de tâches asynchrones.

QtTaskTree::GroupItem

Représente l'élément de base qui peut faire partie de n'importe quel groupe.

QtTaskTree::If

Un élément "if" utilisé dans les expressions conditionnelles

QtTaskTree::Iterator

Classe de base à utiliser comme itérateur à l'intérieur de l'élément For

QtTaskTree::ListIterator

Itérateur de liste à utiliser dans l'élément For

QtTaskTree::ObjectSignal

Structure décrivant la sous-classe QObject et son signal

QtTaskTree::ParallelLimit

Mode d'exécution parallèle avec une limite personnalisée

QtTaskTree::QBarrier

Une tâche asynchrone qui se termine à la demande

QtTaskTree::QCustomTask

Un modèle de classe utilisé pour déclarer des éléments de tâche personnalisés et définir leurs gestionnaires de configuration et d'exécution.

QtTaskTree::QDefaultTaskAdapter

Un modèle de classe fournissant l'adaptateur de tâche par défaut utilisé dans QCustomTask

QtTaskTree::QMappedTaskTreeRunner

Un contrôleur d'exécution d'arbre de tâches mappé avec un type de clé donné

QtTaskTree::QNetworkReplyWrapper

Une enveloppe autour de QNetworkReply et QNetworkAccessManager

QtTaskTree::QParallelTaskTreeRunner

Un contrôleur d'exécution d'arbre de tâches parallèle

QtTaskTree::QProcessTaskDeleter

Un suppresseur personnalisé pour QProcess, utilisé par QProcessTask

QtTaskTree::QSequentialTaskTreeRunner

Un contrôleur d'exécution d'arbre de tâches séquentiel

QtTaskTree::QSingleTaskTreeRunner

Un contrôleur d'exécution d'arbre de tâches unique

QtTaskTree::QStartedBarrier

Un QBarrier démarré avec une limite donnée

QtTaskTree::QSyncTask

Exécution synchrone d'un gestionnaire personnalisé entre d'autres tâches

QtTaskTree::QTaskInterface

Classe d'aide utilisée lors de l'adaptation de l'interface d'une tâche personnalisée

QtTaskTree::QTaskTree

Exécute l'arbre des tâches asynchrones définies de manière déclarative.

QtTaskTree::QTcpSocketWrapper

Une enveloppe autour de QTcpSocket

QtTaskTree::QThreadFunction

Un modèle de classe contrôlant l'exécution d'une fonction dans un thread séparé via QtConcurrent::run()

QtTaskTree::QThreadFunctionBase

Une classe de base pour le modèle de classe QThreadFunction

QtTaskTree::RepeatIterator

Itérateur répétitif à utiliser à l'intérieur de l'élément For

QtTaskTree::Storage

Un modèle de classe pour l'échange de données personnalisées dans l'arbre des tâches en cours d'exécution

QtTaskTree::Then

Un élément "then" utilisé dans les expressions conditionnelles

QtTaskTree::UntilIterator

Itérateur conditionnel à utiliser dans l'élément For

QtTaskTree::When

Élément retardant l'exécution d'un corps jusqu'à l'avance de la barrière

Description détaillée

Utiliser la bibliothèque TaskTree pour construire des recettes, décrivant les tâches asynchrones à exécuter, et utiliser ces recettes dans QTaskTree pour les exécuter.

Les recettes sont des descriptions déclaratives des types de tâches à créer et à exécuter, par exemple : QProcess, QNetworkReplyWrapper, ou QThreadFunction<ReturnType>, ou si elles doivent être exécutées en séquence ou en parallèle. À l'intérieur des recettes, vous pouvez définir différents chemins de continuation selon que la tâche précédente s'est terminée par un succès ou une erreur. Il est également possible d'imbriquer des tâches dans des éléments Group, et chaque Group peut exécuter ses tâches selon son propre mode d'exécution ou sa propre politique de flux de travail. Les recettes forment les structures de l'arbre des tâches.

Tâches asynchrones

Une tâche asynchrone est une tâche qui peut être lancée et qui se termine plus tard par un succès ou une erreur. Plus tard signifie qu'après le démarrage de la tâche, le contrôle revient à la boucle d'événements en cours d'exécution. Nous ne bloquons pas le thread de l'appelant jusqu'à ce que la tâche soit terminée. Pour utiliser l'arbre des tâches, nous avons besoin d'une boucle événementielle.

Exemples de tâches asynchrones :

Recette et arbre des tâches

Afin de mémoriser ce que sont la recette et l'arbre des tâches, utilisons l'analogie avec la cartouche et le lecteur. Lorsque nous écrivons une recette, c'est comme si nous construisions une cartouche, nous préparons donc une description détaillée de ce que le lecteur devra faire plus tard, lorsque la cartouche sera placée dans un lecteur (arbre des tâches) et démarrée. La recette elle-même est juste une description déclarative pour l'arbre des tâches sur ce que l'arbre des tâches doit faire quand la recette est passée à l'arbre des tâches et que l'arbre des tâches est démarré. La recette elle-même ne fait rien sans l'arbre des tâches - c'est comme une cartouche sans lecteur.

Voici un bref résumé des responsabilités des recettes et des arbres de tâches.

La recette (cartouche) décrit :

  • quelles tâches doivent être créées dynamiquement par l'arbre des tâches en cours d'exécution (via QCustomTask)
  • dans quel ordre
  • quelles structures de données doivent être créées dynamiquement par l'arbre des tâches en cours d'exécution (via Storage)
  • comment configurer chaque tâche avant le démarrage
  • comment collecter les données lorsque les tâches sont terminées
  • le mode d'exécution (les tâches doivent être exécutées en séquence ou en parallèle)
  • la politique en matière de flux de travail

l'arbre des tâches (lecteur) :

  • lit la recette et crée automatiquement des tâches et des structures de données
  • gère la durée de vie des tâches et des structures de données créées
  • exécute les continuations
  • choisit différents chemins en fonction des résultats des tâches terminées et des politiques de flux de travail
  • fournit des informations de base sur l'avancement des travaux

Tâches personnalisées

Puisque la recette est une description pour l'arbre des tâches sur les tâches qu'il doit créer lorsque l'arbre des tâches est démarré, nous ne pouvons pas créer ces tâches directement à l'intérieur d'une recette. Au lieu de cela, nous avons besoin d'un moyen déclaratif pour dire à l'arbre des tâches de créer et de démarrer ces tâches pour nous à un moment ultérieur. Par exemple, si nous voulons que l'arbre des tâches crée et démarre QProcess, nous le décrivons en plaçant l'élément QProcessTask dans une recette. L'élément QProcessTask est un alias de l'élément QCustomTask<QProcess>. Chaque type de tâche doit fournir l'élément QCustomTask<Type> correspondant afin qu'il puisse être utilisé dans les recettes.

Le tableau suivant présente quelques tâches personnalisées prêtes à être placées dans des recettes :

Tâche personnalisée (utilisée dans les recettes)Classe de tâche (créée par l'arbre des tâches en cours d'exécution)Brève description
QProcessTaskQProcessDémarre le processus.
QThreadFunctionTask<ReturnType>QThreadFunction<ReturnType>Démarre une tâche asynchrone, s'exécute dans un thread séparé.
QTaskTreeTaskQTaskTreeDémarre l'arbre des tâches imbriquées.
QNetworkReplyWrapperTaskQNetworkReplyWrapperDémarre le téléchargement réseau.
QTcpSocketWrapperTaskQTcpSocketWrapperDémarre une connexion TCP.

Voir QTaskInterface et Task Adapters pour plus d'informations sur la manière d'adapter une tâche particulière pour l'utiliser dans des recettes.

Exemple de recette

Le site QTaskTree comporte un élément de premier niveau, Group, c'est-à-dire une recette, qui peut contenir un nombre quelconque de tâches de différents types, telles que QProcessTask, QNetworkReplyWrapperTask, ou QThreadFunctionTask<ReturnType> :

const Group recipe {
    QProcessTask(...),
    QNetworkReplyWrapperTask(...),
    QThreadFunctionTask<int>(...)
};

QTaskTree *taskTree = new QTaskTree(recipe);
connect(taskTree, &QTaskTree::done, ...);  // finish handler
taskTree->start();

La recette ci-dessus consiste en un élément de premier niveau de type Group qui contient des tâches de type QProcessTask, QNetworkReplyWrapperTask, et QThreadFunctionTask<int>. Après l'appel de taskTree->start(), les tâches sont créées et exécutées en chaîne, en commençant par QProcess. Lorsque la tâche QProcess se termine avec succès, la tâche QNetworkReplyWrapper est lancée. Enfin, lorsque la tâche réseau se termine avec succès, la tâche QThreadFunction<int> est lancée.

Lorsque la dernière tâche en cours d'exécution se termine avec succès, l'arbre des tâches est considéré comme s'étant exécuté avec succès et le signal QTaskTree::done() est émis avec DoneWith::Success. Lorsqu'une tâche se termine par une erreur, l'exécution de l'arborescence des tâches est arrêtée et les tâches restantes sont ignorées. L'arbre des tâches se termine par une erreur et envoie le signal QTaskTree::done() à l'adresse DoneWith::Error.

Groupes

Le parent du groupe Group le considère comme une tâche unique. Comme les autres tâches, le groupe peut être lancé et se terminer par un succès ou une erreur. Les éléments Group peuvent être imbriqués pour créer une structure arborescente :

const Group recipe {
    Group {
        parallel,
        QProcessTask(...),
        QThreadFunctionTask<int>(...)
    },
    QNetworkReplyWrapperTask(...)
};

L'exemple ci-dessus diffère du premier exemple en ce sens que l'élément de niveau supérieur a un sous-groupe qui contient les éléments QProcessTask et QThreadFunctionTask<int>. Le sous-groupe est un élément frère de l'élément QNetworkReplyWrapperTask de la racine. Le sous-groupe contient un élément supplémentaire parallel qui demande à son Group d'exécuter ses tâches en parallèle.

Ainsi, lorsque QTaskTree lance la recette ci-dessus, QProcess et QThreadFunction<int> démarrent immédiatement et s'exécutent en parallèle. Étant donné que le groupe racine ne contient pas d'élément parallel, les tâches de ses enfants directs sont exécutées dans l'ordre. Ainsi, la tâche QNetworkReplyWrapper démarre lorsque le sous-groupe entier se termine. Le groupe est considéré comme terminé lorsque toutes ses tâches sont terminées. L'ordre dans lequel les tâches se terminent n'a pas d'importance.

Ainsi, selon la tâche qui dure le plus longtemps (QProcess ou QThreadFunction<int>), les scénarios suivants peuvent se produire :

Scénario 1Scénario 2
Le groupe racine commenceLe groupe racine commence
Début du sous-groupeDémarrage du sous-groupe
QProcess démarreQProcess démarre
QThreadFunction<int> démarreQThreadFunction<int> démarre
......
QProcess termineQThreadFunction<int> termine
......
QThreadFunction<int> finitionsQProcess finitions
Sous-groupe termineLe sous-groupe se termine
QNetworkReplyWrapper démarreQNetworkReplyWrapper démarre
......
QNetworkReplyWrapper termineQNetworkReplyWrapper termine
Fin du groupe racineLe groupe racine termine

Les différences entre les scénarios sont indiquées en gras. Trois points signifient qu'un laps de temps non spécifié s'écoule entre l'événement précédent et l'événement suivant (une ou plusieurs tâches continuent de s'exécuter). L'absence de points entre les événements signifie qu'ils se produisent de manière synchrone.

Les scénarios présentés supposent que toutes les tâches s'exécutent avec succès. Si une tâche échoue en cours d'exécution, l'arbre des tâches se termine par une erreur. En particulier, lorsque QProcess se termine par une erreur alors que QThreadFunction<int> est toujours en cours d'exécution, QThreadFunction<int> est automatiquement annulée, le sous-groupe se termine par une erreur, QNetworkReplyWrapper est ignoré et l'arbre se termine par une erreur.

Gestionnaires de tâches

Utilisez les gestionnaires Task pour configurer l'exécution d'une tâche et pour permettre la lecture des données de sortie de la tâche lorsqu'elle se termine par un succès ou une erreur.

Gestionnaire de démarrage de la tâche

Lorsqu'un objet tâche est créé et avant qu'il ne soit lancé, l'arbre des tâches invoque un gestionnaire de configuration optionnellement fourni par l'utilisateur. Le gestionnaire d'installation doit toujours prendre une référence à l'objet de classe de tâche associé :

const auto onSetup = [](QProcess &process) {
    process.setProgram("sleep");
    process.setArguments({"3"});
};
const Group root {
    QProcessTask(onSetup)
};

Vous pouvez modifier l'adresse QProcess passée dans le gestionnaire d'installation, de sorte que l'arbre des tâches puisse démarrer le processus selon votre configuration. Vous ne devez pas appeler process.start(); dans le gestionnaire d'installation, car l'arbre des tâches l'appelle en cas de besoin. Le gestionnaire d'installation est facultatif. Lorsqu'il est utilisé, il doit être le premier argument du constructeur de la tâche.

Optionnellement, le gestionnaire d'installation peut renvoyer un SetupResult. Le SetupResult renvoyé influence le comportement de démarrage ultérieur d'une tâche donnée. Les valeurs possibles sont les suivantes :

SetupResult ValeurBrève description
ContinueLa tâche sera démarrée normalement. C'est le comportement par défaut lorsque le gestionnaire d'installation ne renvoie pas SetupResult (c'est-à-dire que son type de retour est void).
StopWithSuccessLa tâche ne sera pas lancée et signalera un succès à son parent.
StopWithErrorLa tâche ne sera pas lancée et signalera une erreur à son parent.

Ceci est utile pour exécuter une tâche uniquement lorsqu'une condition est remplie et que les données nécessaires à l'évaluation de cette condition ne sont pas connues tant que les tâches précédemment lancées ne sont pas terminées. De cette manière, le gestionnaire de configuration décide dynamiquement de lancer la tâche correspondante normalement ou de l'ignorer et de signaler un succès ou une erreur. Pour plus d'informations sur l'échange de données entre tâches, voir Storage.

Gestionnaire de tâches terminées

Lorsqu'une tâche en cours d'exécution se termine, l'arbre des tâches invoque un gestionnaire de tâches (done handler) fourni de manière facultative. Le gestionnaire doit prendre une référence const à l'objet de classe de tâche associé :

const auto onSetup = [](QProcess &process) { process.setProgram("sleep") ; process.setArguments({"3"}) ; } ;const auto onDone = [](const QProcess &process, DoneWith result) { if (result == DoneWith::Success)        qDebug() << "Success" << process.cleanedStdOut();
   autre        qDebug() << "Failure" << process.cleanedStdErr();
} ;const Group root { QProcessTask(onSetup, onDone) } ;

Le gestionnaire de tâches terminées peut collecter des données de sortie à partir de QProcess et les stocker en vue d'un traitement ultérieur ou de l'exécution d'autres actions.

Remarque : si le gestionnaire de configuration de la tâche renvoie StopWithSuccess ou StopWithError, le gestionnaire done n'est pas invoqué.

Gestionnaires de groupe

De la même manière que les gestionnaires de tâches, les gestionnaires de groupes vous permettent de configurer un groupe à exécuter et d'appliquer d'autres actions lorsque le groupe entier se termine par un succès ou une erreur.

Gestionnaire de démarrage de groupe

L'arbre des tâches invoque le gestionnaire de démarrage de groupe avant de lancer les tâches enfants. Le gestionnaire de groupe ne prend aucun argument :

const auto onSetup = [] {    qDebug() << "Entering the group";
} ;const Group root { onGroupSetup(onSetup), QProcessTask(...) } ;

Le gestionnaire de configuration de groupe est facultatif. Pour définir un gestionnaire de configuration de groupe, ajoutez un élément onGroupSetup() à un groupe. L'argument de onGroupSetup() est un gestionnaire d'utilisateur. Si vous ajoutez plus d'un élément onGroupSetup() à un groupe, un assert est déclenché au moment de l'exécution et comprend un message d'erreur.

Comme le gestionnaire de démarrage de la tâche, le gestionnaire de démarrage du groupe peut renvoyer SetupResult. La valeur renvoyée SetupResult affecte le comportement de démarrage de l'ensemble du groupe. Si vous ne spécifiez pas de gestionnaire de démarrage de groupe ou si son type de retour est void, l'action par défaut du groupe est Continue, de sorte que toutes les tâches sont démarrées normalement. Sinon, lorsque le gestionnaire de démarrage renvoie StopWithSuccess ou StopWithError, les tâches ne sont pas lancées (elles sont ignorées) et le groupe lui-même signale un succès ou une erreur, en fonction de la valeur renvoyée, respectivement.

const Group root {
    onGroupSetup([] { qDebug() << "Root setup"; }),
    Group {
        onGroupSetup([] { qDebug() << "Group 1 setup"; return SetupResult::Continue; }),
        QProcessTask(...) // Process 1
    },
    Group {
        onGroupSetup([] { qDebug() << "Group 2 setup"; return SetupResult::StopWithSuccess; }),
        QProcessTask(...) // Process 2
    },
    Group {
        onGroupSetup([] { qDebug() << "Group 3 setup"; return SetupResult::StopWithError; }),
        QProcessTask(...) // Process 3
    },
    QProcessTask(...) // Process 4
};

Dans l'exemple ci-dessus, tous les sous-groupes d'un groupe racine définissent leurs gestionnaires de configuration. Le scénario suivant suppose que tous les processus lancés se terminent avec succès :

ScénarioCommentaire
Le groupe racine démarreNe renvoie pas de SetupResult, ses tâches sont donc exécutées.
Le groupe 1 démarreRenvoie Continue, ses tâches sont donc exécutées.
Le processus 1 démarre
......
Le processus 1 se termine (succès)
Le groupe 1 se termine (succès)
Le groupe 2 démarreRetourne StopWithSuccess, le processus 2 est donc ignoré et le groupe 2 rapporte un succès.
Le groupe 2 termine (succès)
Le groupe 3 démarreRenvoie StopWithError, de sorte que le processus 3 est ignoré et que le groupe 3 signale une erreur.
Le groupe 3 termine (erreur)
Le groupe racine se termine (erreur)Le groupe 3, qui est un enfant direct du groupe racine, s'est terminé avec une erreur. Le groupe racine arrête donc de s'exécuter, ignore le processus 4, qui n'a pas encore démarré, et signale une erreur.

Gestionnaire "Done" des groupes

Le gestionnaire "done" d'un site Group est exécuté après la réussite ou l'échec de l'exécution de ses tâches. La valeur finale rapportée par le groupe dépend de son Workflow Policy. Le gestionnaire peut appliquer d'autres actions nécessaires. Le gestionnaire d'exécution est défini dans l'élément onGroupDone() d'un groupe. Il peut prendre l'argument facultatif DoneWith, qui indique la réussite ou l'échec de l'exécution :

const Group root { onGroupSetup([] { qDebug()<< "Root setup"; }), QProcessTask(...),onGroupDone([](DoneWith result) { if (result == DoneWith::Success)            qDebug() << "Root finished with success";
       autre            qDebug() << "Root finished with an error";
    }) } ;

Le gestionnaire de groupe est facultatif. Si vous ajoutez plus d'un onGroupDone() à un groupe, un assert est déclenché à l'exécution et inclut un message d'erreur.

Remarque : même si le gestionnaire de configuration du groupe renvoie StopWithSuccess ou StopWithError, le gestionnaire d'exécution du groupe est invoqué. Ce comportement diffère de celui du gestionnaire de tâches done et pourrait changer à l'avenir.

Autres éléments de groupe

Un groupe peut contenir d'autres éléments décrivant le flux de traitement, tels que execution mode ou workflow policy. Il peut également contenir des éléments de stockage chargés de collecter et de partager les données communes personnalisées recueillies au cours de l'exécution du groupe.

Mode d'exécution

L'élément mode d'exécution d'un groupe spécifie la manière dont les tâches enfant directes du groupe sont lancées. Les modes d'exécution les plus courants sont sequential et parallel. Il est également possible de spécifier la limite des tâches exécutées en parallèle en utilisant l'élément ParallelLimit.

Dans tous les modes d'exécution, un groupe démarre les tâches dans l'ordre dans lequel elles apparaissent.

Si un enfant d'un groupe est également un groupe, le groupe enfant exécute ses tâches selon son propre mode d'exécution.

Politique de flux de travail

L'élément de politique de flux de travail d'un site Group spécifie comment le groupe doit se comporter lorsque l'une des tâches de ses enfants directs est terminée. Pour une description détaillée des politiques possibles, voir WorkflowPolicy.

Si un enfant d'un groupe est également un groupe, le groupe enfant exécute ses tâches conformément à sa propre politique de flux de travail.

Stockage

Utilisez l'élément Storage pour échanger des informations entre les tâches. En particulier, dans le mode d'exécution séquentiel, lorsqu'une tâche a besoin des données d'une autre tâche déjà terminée avant de pouvoir commencer. Par exemple, un arbre de tâches qui copie des données en les lisant à partir d'une source et en les écrivant à une destination peut se présenter comme suit :

statique QByteArray load(const QString &fileName) { ... }static void save(const QString &fileName, const QByteArray &array) { ... }static Group copyRecipe(const QString &source, const QString &destination) { struct CopyStorage { // [1] structure inter-tâches personnalisée       QByteArray content ; // [2] données inter-tâches  personnalisées } ; // [3] instance de structure inter-tâches personnalisée gérable par l'arbre des tâches const  Storage<CopyStorage> storage ; const auto onLoaderSetup = [source](QThreadFunction<QByteArray> &async) { async.setThreadFunctionData(&load, source) ; } ; // [4] temps d'exécution : l'arbre des tâches active l'instance de [7] avant d'invoquer le gestionnaire const auto onLoaderDone = [storage](const  QThreadFunction<QByteArray> &async) {  storage->content = async.result() ; // [5] le chargeur stocke le résultat dans le stockage} ; // [4] runtime : l'arbre des tâches active l'instance de [7] avant d'invoquer le handler const auto onSaverSetup = [storage, destination](QThreadFunction<void> &async) { const QByteArray content =  storage->content; // [6] saver prend les données du stockageasync.setThreadFunctionData(&save, destination, content) ; } ; const auto onSaverDone = [](const  QThreadFunction<void> &async) {        qDebug() << "Save done successfully";
    } ; const Group root { // [7] runtime : task tree creates an instance of CopyStorage when root is enteredstorage, QThreadFunctionTask<QByteArray>(onLoaderSetup, onLoaderDone, CallDoneFlag::OnSuccess),QThreadFunctionTask<void>(onSaverSetup, onSaverDone, CallDoneFlag::OnSuccess) } ; return root ; }...const QString source = ...;const QString destination = ...;QTaskTree taskTree(copyRecipe(source, destination)) ; connect(&taskTree, &QTaskTree::done, &taskTree, [](DoneWith result) { if (result == DoneWith::Success)        qDebug() << "The copying finished successfully.";
}) ; tasktree.start() ;

Dans l'exemple ci-dessus, les données inter-tâches consistent en une variable de contenu QByteArray [2] incluse dans une structure personnalisée CopyStorage [1]. Si le chargeur se termine avec succès, il stocke les données dans une variable CopyStorage::content [5]. L'épargnant utilise ensuite la variable pour configurer la tâche d'enregistrement [6].

Pour permettre à un arbre de tâches de gérer la structure CopyStorage, une instance de Storage<CopyStorage> est créée [3]. Si une copie de cet objet est insérée en tant qu'élément enfant du groupe [7], une instance de la structure CopyStorage est créée dynamiquement lorsque l'arbre des tâches entre dans ce groupe. Lorsque l'arbre des tâches quitte ce groupe, l'instance existante de la structure CopyStorage est détruite car elle n'est plus nécessaire.

Si plusieurs arbres de tâches détenant une copie de l'instance commune Storage<CopyStorage> s'exécutent simultanément (y compris dans le cas où les arbres de tâches sont exécutés dans des threads différents), chaque arbre de tâches contient sa propre copie de la structure CopyStorage.

Vous pouvez accéder à CopyStorage à partir de n'importe quel gestionnaire du groupe avec un objet de stockage. Cela inclut tous les gestionnaires de toutes les tâches descendantes du groupe avec un objet de stockage. Pour accéder à la structure personnalisée dans un gestionnaire, passez la copie de l'objet Storage<CopyStorage> au gestionnaire (par exemple, dans une capture lambda) [4].

Lorsque l'arbre des tâches invoque un gestionnaire dans un sous-arbre contenant le stockage [7], l'arbre des tâches active sa propre instance CopyStorage à l'intérieur de l'objet Storage<CopyStorage>. Par conséquent, on ne peut accéder à la structure CopyStorage qu'à partir du corps du gestionnaire. Pour accéder à l'instance CopyStorage actuellement active à partir de Storage<CopyStorage>, utilisez la méthode Storage::operator->(), Storage::operator*() ou Storage::activeStorage().

La liste suivante résume la manière d'employer un objet Storage dans l'arbre des tâches :

  1. Définir la structure personnalisée MyStorage avec des données personnalisées [1], [2]
  2. Créer une instance de l'objet de stockage Storage<MyStorage> [3].
  3. Transmettre l'instance Storage<MyStorage> aux gestionnaires [4].
  4. Accéder à l'instance MyStorage dans les gestionnaires [5], [6]
  5. Insérer l'instance Storage<MyStorage> dans un groupe [7]

Classe QTaskTree

QTaskTree exécute l'arborescence des tâches asynchrones selon la recette décrite par l'élément racine Group.

Comme QTaskTree est également une tâche asynchrone, elle peut faire partie d'une autre QTaskTree. Pour placer un QTaskTree imbriqué dans un autre QTaskTree, insérez l'élément QTaskTreeTask dans un autre élément Group.

QTaskTree rapporte la progression des tâches achevées lorsqu'elles sont en cours d'exécution. La valeur de la progression est augmentée lorsqu'une tâche se termine, est ignorée ou annulée. Lorsque QTaskTree est terminé et que le signal QTaskTree::done() est émis, la valeur actuelle de la progression est égale à la valeur maximale de la progression. La progression maximale est égale au nombre total de tâches asynchrones dans un arbre. Un site QTaskTree imbriqué est compté comme une tâche unique et les tâches de ses enfants ne sont pas comptées dans l'arbre de niveau supérieur. Les groupes eux-mêmes ne sont pas comptés comme des tâches, mais leurs tâches sont comptées. Les tâches QSyncTask ne sont pas asynchrones, elles ne sont donc pas comptées comme des tâches.

Pour définir des données initiales supplémentaires pour l'arbre en cours d'exécution, modifiez les instances de stockage dans un arbre lorsqu'il les crée en installant un gestionnaire de configuration de stockage :

Storage<CopyStorage> storage;
const Group root = ...; // storage placed inside root's group and inside handlers
QTaskTree taskTree(root);
auto initStorage = [](CopyStorage &storage) {
    storage.content = "initial content";
};
taskTree.onStorageSetup(storage, initStorage);
taskTree.start();

Lorsque l'arbre des tâches en cours d'exécution crée une instance CopyStorage, et avant que tout gestionnaire à l'intérieur d'un arbre ne soit appelé, l'arbre des tâches appelle le gestionnaire initStorage, pour permettre la configuration des données initiales du stockage, unique à cette exécution particulière de taskTree.

De même, pour collecter des données de résultats supplémentaires à partir de l'arbre en cours d'exécution, lisez-les à partir des instances de stockage de l'arbre lorsqu'elles sont sur le point d'être détruites. Pour ce faire, installez un gestionnaire d'instance de stockage (storage done handler) :

Storage<CopyStorage> storage ;const Group root = ...; // stockage placé dans le groupe de la racine et dans les gestionnairesQTaskTree taskTree(root) ;auto collectStorage = [](const CopyStorage &storage) {    qDebug() << "final content" << storage.content;
} ; taskTree.onStorageDone(storage, collectStorage) ; taskTree.start() ;

Lorsque l'arbre des tâches en cours d'exécution est sur le point de détruire une instance CopyStorage, il appelle le gestionnaire collectStorage, afin de permettre la lecture des données finales du stockage, propres à cette exécution particulière de taskTree.

Adaptateurs de tâches

Il est assez facile de permettre à de nouveaux types de tâches de faire partie des recettes. Il suffit de définir un nouvel alias de tâche au modèle QCustomTask, en passant votre type Task comme premier argument du modèle, comme :

class Worker : public QObject
{
public:
    void start() { ... }

signals:
    void done(bool result);
};

using WorkerTask = QCustomTask<Worker>;

Cela fonctionnera si les conditions suivantes sont remplies :

  1. Votre tâche est dérivée de QObject.
  2. Votre tâche possède une méthode publique start() qui démarre la tâche.
  3. Votre tâche émet un signal done(bool) ou done(DoneResult) lorsqu'elle est terminée.

Si votre tâche ne remplit pas ces conditions, vous pouvez toujours l'adapter pour qu'elle fonctionne avec le cadre TaskTree, en fournissant un deuxième argument de modèle avec l'adaptateur personnalisé. Disons que nous voulons adapter QTimer pour qu'il fonctionne avec TaskTree. Le site Adapter pourrait ressembler à ceci :

class TimerAdapter
{
public:
    void operator()(QTimer *task, QTaskInterface *iface) {
        task->setSingleShot(true);
        QObject::connect(task, &QTimer::timeout, iface, [iface] {
            iface->reportDone(DoneResult::Success);
        });
        task->start();
    }
};

using TimerTask = QCustomTask<QTimer, TimerAdapter>;

Vous pouvez maintenant commencer à utiliser le TimerTask dans vos recettes, comme par exemple :

const auto onSetup = [](QTimer &task) { task.setInterval(2000) ; } ;const auto onDone = [](const QTimer &task) {    qDebug() << "Timer triggered after" << task.interval() << "ms.";
} ;const Group recipe { TimerTask(onSetup, onDone) } ;

Remarque : la classe qui met en œuvre la tâche en cours doit avoir un constructeur par défaut et les objets de cette classe doivent être librement destructibles. Elle doit être autorisée à détruire une tâche en cours d'exécution, de préférence sans attendre la fin de la tâche en cours d'exécution (c'est-à-dire un destructeur sûr et non bloquant d'une tâche en cours d'exécution). Pour obtenir une destruction non bloquante d'une tâche ayant un destructeur bloquant, il est possible d'utiliser le paramètre de modèle optionnel Deleter de QCustomTask (le troisième argument de modèle).

Exécutants de l'arbre des tâches

Le gestionnaire de l'arbre des tâches gère la durée de vie du site QTaskTree sous-jacent utilisé pour exécuter la recette donnée.

Le tableau suivant résume les différences entre les différents gestionnaires d'arborescence des tâches :

Nom de la classeDescription
QSingleTaskTreeRunnerGère l'exécution d'une seule arborescence de tâches. La méthode QSingleTaskTreeRunner::start() démarre inconditionnellement la recette transmise, en réinitialisant tout arbre des tâches en cours d'exécution. Un seul arbre des tâches peut être exécuté à la fois.
QSequentialTaskTreeRunnerGère les exécutions séquentielles de l'arbre des tâches. La méthode QSequentialTaskTreeRunner::enqueue() démarre la recette transmise si l'exécutant de l'arbre des tâches est inactif. Sinon, la recette est mise en file d'attente. Lorsque la tâche en cours se termine, le gestionnaire exécute séquentiellement la recette mise en attente. Un seul arbre de tâches peut être exécuté à la fois.
QParallelTaskTreeRunnerGère les exécutions parallèles de l'arbre des tâches. La méthode QParallelTaskTreeRunner::start() démarre inconditionnellement la recette transmise et maintient en parallèle les éventuels arbres de tâches en cours d'exécution.
QMappedTaskTreeRunnerGère les exécutions d'arbres de tâches mappés. La méthode QMappedTaskTreeRunner::start() lance inconditionnellement la recette spécifiée pour une clé donnée. Si un autre arbre des tâches est déjà en cours d'exécution avec la même clé, il sera réinitialisé. Les arbres de tâches ayant des clés différentes ne sont pas affectés et continuent leur exécution.

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.