En esta página

Qt TaskTree C++ Classes

Contiene una biblioteca TaskTree de propósito general. Más...

Este módulo está en desarrollo y está sujeto a cambios.

Este módulo se introdujo en Qt 6.11.

Espacios de nombres

QtTaskTree

Engloba todas las clases y funciones globales del módulo TaskTree

Clases

QtTaskTree::Do

Un elemento body utilizado con las construcciones For y When

QtTaskTree::Else

Un elemento "else" utilizado en expresiones condicionales

QtTaskTree::ElseIf

Elemento "else if" utilizado en expresiones condicionales

QtTaskTree::ExecutableItem

Clase base para elementos de tarea ejecutables

QtTaskTree::ExecutionMode

Elemento de grupo que describe el modo de ejecución

QtTaskTree::For

Un elemento de bucle for

QtTaskTree::Forever

Bucle infinito de subtareas

QtTaskTree::ForeverIterator

Iterador infinito que se utiliza dentro del elemento For

QtTaskTree::Group

Representa el elemento básico para componer recetas declarativas que describen cómo ejecutar y manejar un árbol anidado de tareas asíncronas

QtTaskTree::GroupItem

Representa el elemento básico que puede formar parte de cualquier Grupo

QtTaskTree::If

Elemento "if" utilizado en expresiones condicionales

QtTaskTree::Iterator

Clase base para ser utilizada como iterador dentro del elemento For

QtTaskTree::ListIterator

Iterador de lista a utilizar dentro del elemento For

QtTaskTree::ObjectSignal

Estructura que describe la subclase QObject y su señal

QtTaskTree::ParallelLimit

Modo de ejecución en paralelo con un límite personalizado

QtTaskTree::QBarrier

Una tarea asíncrona que finaliza bajo demanda

QtTaskTree::QCustomTask

Una plantilla de clase utilizada para declarar elementos de tarea personalizados y definir sus manejadores de configuración y finalización

QtTaskTree::QDefaultTaskAdapter

Una plantilla de clase que proporciona el adaptador de tarea por defecto utilizado en QCustomTask

QtTaskTree::QMappedTaskTreeRunner

Un controlador de ejecución de árbol de tareas mapeado con un tipo de clave dado

QtTaskTree::QNetworkReplyWrapper

Una envoltura alrededor de QNetworkReply y QNetworkAccessManager

QtTaskTree::QParallelTaskTreeRunner

Un controlador de ejecución de árbol de tareas paralelo

QtTaskTree::QProcessTaskDeleter

Un eliminador personalizado para QProcess, utilizado por QProcessTask

QtTaskTree::QSequentialTaskTreeRunner

Un controlador secuencial de la ejecución del árbol de tareas

QtTaskTree::QSingleTaskTreeRunner

Un controlador de ejecución de árbol de tareas simple

QtTaskTree::QStartedBarrier

Un QBarrier iniciado con un límite dado

QtTaskTree::QSyncTask

Ejecuta sincrónicamente un controlador personalizado entre otras tareas

QtTaskTree::QTaskInterface

Clase auxiliar utilizada al adaptar la interfaz de la tarea personalizada

QtTaskTree::QTaskTree

Ejecuta el árbol de tareas asíncronas definidas de forma declarativa

QtTaskTree::QTcpSocketWrapper

Una envoltura alrededor de QTcpSocket

QtTaskTree::QThreadFunction

Una plantilla de clase que controla la ejecución de una función en un hilo separado a través de QtConcurrent::run()

QtTaskTree::QThreadFunctionBase

Una clase base para la plantilla de clase QThreadFunction

QtTaskTree::RepeatIterator

Iterador repetitivo para ser utilizado dentro del elemento For

QtTaskTree::Storage

Una plantilla de clase para el intercambio de datos personalizados en el árbol de tareas en ejecución

QtTaskTree::Then

Un elemento "then" utilizado en expresiones condicionales

QtTaskTree::UntilIterator

Iterador condicional a utilizar dentro del elemento For

QtTaskTree::When

Un elemento que retrasa la ejecución de un cuerpo hasta que avance la barrera

Descripción detallada

Utiliza la librería TaskTree para construir recetas, describiendo qué tareas asíncronas deben ejecutarse, y utiliza estas recetas dentro de QTaskTree para ejecutarlas.

Las recetas son descripciones declarativas sobre qué tipos de tareas deben crearse y ejecutarse, por ejemplo QProcess, QNetworkReplyWrapper, o QThreadFunction<ReturnType>, o si deben ejecutarse en secuencia o en paralelo. Dentro de las recetas se pueden definir diferentes rutas de continuación dependiendo de si la tarea anterior terminó con éxito o con un error. También es posible anidar tareas en elementos de Group, y cada Group puede ejecutar sus tareas según su propio modo de ejecución o política de flujo de trabajo. Las recetas forman las estructuras del árbol de tareas.

Tareas asíncronas

Una tarea asíncrona es cualquier tarea que puede iniciarse y más tarde finalizar con éxito o con un error. Más tarde significa que después de iniciar la tarea, el control vuelve al bucle de eventos en ejecución. No bloqueamos el hilo de llamada hasta que la tarea finaliza. Para utilizar el árbol de tareas necesitamos un bucle de eventos en ejecución.

Ejemplos de Tareas asíncronas:

Árbol de recetas y tareas

Para memorizar lo que es la receta y el árbol de tareas, vamos a utilizar la analogía con el cartucho y el reproductor. Cuando escribimos una receta, es como si estuviéramos construyendo un cartucho, por lo que sólo preparamos una descripción detallada de lo que el jugador debe hacer más tarde, cuando el cartucho se coloca dentro de un reproductor (árbol de tareas) y se inicia. La receta en sí es sólo una descripción declarativa para el árbol de tareas sobre lo que el árbol de tareas debe hacer cuando la receta se pasa al árbol de tareas y el árbol de tareas se inicia. La receta en sí no hace nada por sí misma sin un árbol de tareas, es como un cartucho sin un reproductor.

He aquí un breve resumen sobre las responsabilidades de la receta y del árbol de tareas.

Receta (cartucho) describe:

  • qué tareas deben ser creadas dinámicamente por el árbol de tareas en ejecución (a través de QCustomTask)
  • en qué orden
  • qué estructuras de datos deben ser creadas dinámicamente por el árbol de tareas en ejecución (a través de Storage)
  • cómo configurar cada tarea antes de su inicio
  • cómo recoger los datos cuando las tareas finalizan
  • modo de ejecución (las tareas deben ejecutarse en secuencia o en paralelo)
  • política de flujo de trabajo

Árbol de tareas (reproductor):

  • lee la receta y crea tareas y estructuras de datos automáticamente
  • gestiona el tiempo de vida de las tareas y estructuras de datos creadas
  • ejecuta continuaciones
  • elige diferentes rutas en función de los resultados de las tareas finalizadas y de las políticas de flujo de trabajo
  • proporciona información básica sobre el progreso

Tareas personalizadas

Dado que la receta es una descripción para el árbol de tareas sobre qué tareas debe crear cuando se inicia el árbol de tareas, no podemos crear estas tareas directamente dentro de una receta. En su lugar, necesitamos una forma declarativa de decirle al árbol de tareas que cree e inicie estas tareas por nosotros en un momento posterior. Por ejemplo, si queremos que el árbol de tareas cree e inicie QProcess, lo describimos colocando el elemento QProcessTask dentro de una receta. QProcessTask es un alias de QCustomTask<QProcess>. Cada tipo de tarea debe proporcionar su correspondiente QCustomTask<Type> para que pueda ser utilizado dentro de las recetas.

La siguiente tabla muestra algunas tareas personalizadas listas para ser colocadas dentro de las recetas:

Tarea personalizada (utilizada en recetas)Clase de tarea (creada por el árbol de tareas en ejecución)Breve descripción
QProcessTaskQProcessInicia el proceso.
QThreadFunctionTask<ReturnType>QThreadFunction<ReturnType>Inicia tarea asíncrona, se ejecuta en hilo separado.
QTaskTreeTaskQTaskTreeInicia árbol de tareas anidadas.
QNetworkReplyWrapperTaskQNetworkReplyWrapperInicia la descarga de red.
QTcpSocketWrapperTaskQTcpSocketWrapperInicia una conexión TCP.

Ver QTaskInterface y Task Adapters para más información sobre cómo adaptar una tarea particular para ser usada dentro de recetas.

Ejemplo de receta

QTaskTree tiene un elemento de nivel superior Group, también conocido como receta, que puede contener cualquier número de tareas de varios tipos, como QProcessTask, QNetworkReplyWrapperTask, o QThreadFunctionTask<ReturnType>:

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

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

La receta anterior consiste en un elemento de nivel superior del tipo Group que contiene tareas del tipo QProcessTask, QNetworkReplyWrapperTask, y QThreadFunctionTask<int>. Después de llamar a taskTree->start(), las tareas se crean y se ejecutan en cadena, empezando por QProcess. Cuando la tarea QProcess finaliza con éxito, se inicia la tarea QNetworkReplyWrapper. Por último, cuando la tarea de red finaliza con éxito, se inicia la tarea QThreadFunction<int> .

Cuando la última tarea en ejecución finaliza con éxito, se considera que el árbol de tareas se ha ejecutado correctamente y se emite la señal QTaskTree::done() con DoneWith::Success. Cuando una tarea finaliza con un error, se detiene la ejecución del árbol de tareas y se omiten las tareas restantes. El árbol de tareas finaliza con un error y se emite la señal QTaskTree::done() con DoneWith::Error.

Grupos

El padre de la tarea Group la ve como una única tarea. Al igual que otras tareas, el grupo puede iniciarse y puede finalizar con éxito o con un error. Los elementos de Group pueden anidarse para crear una estructura de árbol:

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

El ejemplo anterior difiere del primero en que el elemento de nivel superior tiene un subgrupo que contiene los elementos QProcessTask y QThreadFunctionTask<int>. El subgrupo es un elemento hermano de QNetworkReplyWrapperTask en la raíz. El subgrupo contiene un elemento parallel adicional que ordena a su Group que ejecute sus tareas en paralelo.

Así, cuando el QTaskTree inicia la receta anterior, el QProcess y QThreadFunction<int> se inician inmediatamente y se ejecutan en paralelo. Como el grupo raíz no contiene un elemento parallel, sus tareas hijas directas se ejecutan en secuencia. Así, el QNetworkReplyWrapper se inicia cuando todo el subgrupo termina. El grupo se considera finalizado cuando todas sus tareas han terminado. El orden de finalización de las tareas no es relevante.

Así, dependiendo de qué tarea dure más (QProcess o QThreadFunction<int>), pueden darse los siguientes escenarios:

Escenario 1Escenario 2
Comienza el Grupo RaízComienza el Grupo Raíz
Inicio del subgrupoInicio del subgrupo
QProcess iniciaQProcess inicia
QThreadFunction<int> startsQThreadFunction<int> inicia
......
QProcess finalizaQThreadFunction<int> finaliza
......
QThreadFunction<int> acabadosQProcess acabados
Sub Group finishesSub Group finaliza
QNetworkReplyWrapper iniciaQNetworkReplyWrapper inicia
......
QNetworkReplyWrapper finalizaQNetworkReplyWrapper finaliza
Root Group finalizaRoot Group finaliza

Las diferencias entre los escenarios están marcadas con negrita. Tres puntos significan que pasa un tiempo indeterminado entre el evento anterior y el siguiente (una o varias tareas siguen ejecutándose). La ausencia de puntos entre los eventos significa que se producen de forma sincrónica.

Los escenarios presentados suponen que todas las tareas se ejecutan correctamente. Si una tarea falla durante la ejecución, el árbol de tareas finaliza con un error. En particular, cuando QProcess finaliza con un error mientras QThreadFunction<int> aún se está ejecutando, QThreadFunction<int> se cancela automáticamente, el subgrupo finaliza con un error, QNetworkReplyWrapper se omite y el árbol finaliza con un error.

Manejadores de tareas

Utilice Task manejadores para configurar una tarea para su ejecución y para permitir la lectura de los datos de salida de la tarea cuando termina con éxito o un error.

Manejador de inicio de tarea

Cuando se crea un objeto de tarea y antes de que se inicie, el árbol de tareas invoca un manejador de configuración proporcionado opcionalmente por el usuario. El manejador de configuración siempre debe tomar una referencia al objeto de clase de tarea asociado:

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

Puedes modificar el QProcess pasado en el manejador de configuración, para que el árbol de tareas pueda iniciar el proceso según tu configuración. No se debe llamar a process.start(); en el manejador de configuración, ya que el árbol de tareas lo llamará cuando sea necesario. El manejador de configuración es opcional. Cuando se utiliza, debe ser el primer argumento del constructor de la tarea.

Opcionalmente, el manejador de configuración puede devolver un SetupResult. El SetupResult devuelto influye en el comportamiento de inicio posterior de una tarea dada. Los valores posibles son:

SetupResult ValorBreve descripción
ContinueLa tarea se iniciará normalmente. Este es el comportamiento por defecto cuando el manejador de configuración no devuelve SetupResult (es decir, su tipo de retorno es void).
StopWithSuccessLa tarea no se iniciará e informará de éxito a su padre.
StopWithErrorLa tarea no se iniciará e informará de un error a su padre.

Esto es útil para ejecutar una tarea sólo cuando se cumple una condición y los datos necesarios para evaluar esta condición no se conocen hasta que finalizan las tareas iniciadas previamente. De este modo, el gestor de configuración decide dinámicamente si iniciar la tarea correspondiente normalmente o saltársela e informar del éxito o de un error. Para obtener más información sobre el intercambio de datos entre tareas, consulte Storage.

Manejador de tarea finalizada

Cuando una tarea en ejecución finaliza, el árbol de tareas invoca a un gestor de tareas finalizadas proporcionado opcionalmente. El manejador debe tomar una referencia const al objeto de clase de tarea asociado:

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

El manejador done puede recoger datos de salida de QProcess, y almacenarlos para su posterior procesamiento o realizar acciones adicionales.

Nota: Si el gestor de configuración de tareas devuelve StopWithSuccess o StopWithError, no se invoca al gestor de tareas realizadas.

Manejadores de grupo

De forma similar a los manejadores de tarea, los manejadores de grupo permiten configurar un grupo para que se ejecute y aplicar más acciones cuando todo el grupo finaliza con éxito o con un error.

Manejador de inicio de grupo

El árbol de tareas invoca al manejador de inicio de grupo antes de iniciar las tareas hijas. El manejador de grupo no toma argumentos:

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

El gestor de configuración de grupo es opcional. Para definir un gestor de configuración de grupo, añade un elemento onGroupSetup() a un grupo. El argumento de onGroupSetup() es un gestor de usuario. Si añades más de un elemento onGroupSetup() a un grupo, se activa un assert en tiempo de ejecución que incluye un mensaje de error.

Al igual que el manejador de inicio de tarea, el manejador de inicio de grupo puede devolver SetupResult. El valor devuelto SetupResult afecta al comportamiento de inicio de todo el grupo. Si no se especifica un gestor de inicio de grupo, o su tipo de retorno es nulo, la acción por defecto del grupo es Continue, de modo que todas las tareas se inician normalmente. De lo contrario, cuando el controlador de inicio devuelve StopWithSuccess o StopWithError, las tareas no se inician (se omiten) y el propio grupo informa de un éxito o un error, dependiendo del valor devuelto, respectivamente.

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
};

En el ejemplo anterior, todos los subgrupos de un grupo raíz definen sus manejadores de inicio. El siguiente escenario asume que todos los procesos iniciados terminan con éxito:

EscenarioComentario
Se inicia el grupo raízNo devuelve SetupResult, por lo que se ejecutan sus tareas.
Se inicia el Grupo 1Devuelve Continue, por lo que se ejecutan sus tareas.
Se inicia el proceso 1
......
El proceso 1 finaliza (éxito)
Grupo 1 finaliza (éxito)
Se inicia el grupo 2Devuelve StopWithSuccess, por lo que el Proceso 2 se salta y el Grupo 2 informa de éxito.
Grupo 2 finaliza (éxito)
Se inicia el grupo 3Devuelve StopWithError, por lo que se omite el proceso 3 y el grupo 3 informa de un error.
El grupo 3 finaliza (error)
El grupo raíz finaliza (error)El grupo 3, que es hijo directo del grupo raíz, finaliza con un error, por lo que el grupo raíz deja de ejecutarse, salta el proceso 4, que aún no se ha iniciado, e informa de un error.

Manejador de Grupos Terminados

El gestor de tareas finalizadas de un grupo Group se ejecuta tras la ejecución correcta o incorrecta de sus tareas. El valor final informado por el grupo depende de su Workflow Policy. El manejador puede aplicar otras acciones necesarias. El gestor de tareas realizadas se define dentro del elemento onGroupDone() de un grupo. Puede tomar el argumento opcional DoneWith, que indica el éxito o el fracaso de la ejecución:

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

El manejador de grupo realizado es opcional. Si añades más de un onGroupDone() a un grupo, se lanza un assert en tiempo de ejecución que incluye un mensaje de error.

Nota: Incluso si el gestor de configuración de grupo devuelve StopWithSuccess o StopWithError, se invoca al gestor de grupo realizado. Este comportamiento difiere del del gestor de tareas realizadas y podría cambiar en el futuro.

Otros elementos de grupo

Un grupo puede contener otros elementos que describen el flujo de procesamiento, como execution mode o workflow policy. También puede contener elementos de almacenamiento que se encargan de recopilar y compartir datos comunes personalizados recopilados durante la ejecución del grupo.

Modo de ejecución

El elemento de modo de ejecución de un Grupo especifica cómo se inician las tareas hijas directas del Grupo. Los modos de ejecución más comunes son sequential y parallel. También es posible especificar el límite de tareas que se ejecutan en paralelo mediante el elemento ParallelLimit.

En todos los modos de ejecución, un grupo inicia las tareas en el orden en que aparecen.

Si un hijo de un grupo es también un grupo, el grupo hijo ejecuta sus tareas según su propio modo de ejecución.

Política de flujo de trabajo

El elemento de política de flujo de trabajo en un Group especifica cómo debe comportarse el grupo cuando finaliza cualquiera de las tareas de sus hijos directos. Para una descripción detallada de las posibles políticas, consulte WorkflowPolicy.

Si un hijo de un grupo es también un grupo, el grupo hijo ejecuta sus tareas de acuerdo con su propia política de flujo de trabajo.

Almacenamiento

Utilice el elemento Storage para intercambiar información entre tareas. Especialmente, en el modo de ejecución secuencial, cuando una tarea necesita datos de otra tarea, ya finalizada, antes de poder comenzar. Por ejemplo, un árbol de tareas que copia datos leyéndolos de un origen y escribiéndolos en un destino podría tener el siguiente aspecto:

static QByteArray load(const QString &fileName) { ... }static void save(const QString &nombrearchivo, const QByteArray &array) { ... }static Group copyRecipe(const QString &fuente, const QString &destino) { struct CopyStorage { // [1] estructura personalizada entre tareas     QByteArray content; // [2] custom inter-task data}; // [3] instance of custom inter-task struct manageable by task tree const  Storage<CopyStorage> storage; const auto onLoaderSetup = [source](QThreadFunction<QByteArray> &async) { async.setThreadFunctionData(&load, source); }; // [4] tiempo de ejecución: el árbol de tareas activa la instancia de [7] antes de invocar al manejador const auto onLoaderDone = [storage](const  QThreadFunction<QByteArray> &async) {  storage->content = async.result(); // [5] loader almacena el resultado en storage}; // [4] runtime: task tree activa lainstancia  desde [7] antes de invocar handler const auto onSaverSetup = [storage, destination](QThreadFunction<void> &async) { const QByteArray content =  storage->content; // [6] saver toma datos del almacenamientoasync.setThreadFunctionData(&save, destination, content); }; const auto onSaverDone = [](const  QThreadFunction<void> &async) {        qDebug() << "Save done successfully";
    }; const Group root { // [7] tiempo de ejecución: el árbol de tareas crea una instancia de CopyStorage cuando se introduce rootstorage, 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();

En el ejemplo anterior, los datos entre tareas consisten en una variable de contenido QByteArray [2] encerrada en una estructura personalizada CopyStorage [1]. Si el cargador finaliza con éxito, almacena los datos en una variable CopyStorage::content [5]. A continuación, el ahorrador utiliza la variable para configurar la tarea de almacenamiento [6].

Para permitir que un árbol de tareas gestione la estructura CopyStorage, se crea una instancia de Storage<CopyStorage> [3]. Si se inserta una copia de este objeto como elemento hijo del grupo [7], se crea dinámicamente una instancia de la estructura CopyStorage cuando el árbol de tareas entra en este grupo. Cuando el árbol de tareas abandona este grupo, la instancia existente de la estructura CopyStorage se destruye porque ya no es necesaria.

Si varios árboles de tareas que contienen una copia de la instancia común Storage<CopyStorage> se ejecutan simultáneamente (incluido el caso en que los árboles de tareas se ejecutan en diferentes hilos), cada árbol de tareas contiene su propia copia de la estructura CopyStorage.

Se puede acceder a CopyStorage desde cualquier manejador del grupo con un objeto de almacenamiento. Esto incluye todos los manejadores de todas las tareas descendientes del grupo con un objeto de almacenamiento. Para acceder a la estructura personalizada en un manejador, pasa la copia del objeto Storage<CopyStorage> al manejador (por ejemplo, en una captura lambda) [4].

Cuando el árbol de tareas invoca un manejador en un subárbol que contiene el almacenamiento [7], el árbol de tareas activa su propia instancia CopyStorage dentro del objeto Storage<CopyStorage>. Por lo tanto, sólo se puede acceder a la estructura CopyStorage desde el cuerpo del manejador. Para acceder al CopyStorage activo actualmente desde dentro de Storage<CopyStorage>, utilice el método Storage::operator->(), Storage::operator*(), o Storage::activeStorage().

La siguiente lista resume cómo emplear un objeto Storage en el árbol de tareas:

  1. Definir la estructura personalizada MyStorage con datos personalizados [1], [2]
  2. Crear una instancia del almacenamiento Storage<MyStorage> [3]
  3. Pasar la instancia Storage<MyStorage> a los manejadores [4]
  4. Acceder a la instancia MyStorage en manejadores [5], [6]
  5. Insertar la instancia Storage<MyStorage> en un grupo [7]

Clase QTaskTree

QTaskTree ejecuta la estructura de árbol de tareas asíncronas según la receta descrita por el elemento raíz Group.

Como QTaskTree también es una tarea asíncrona, puede formar parte de otra QTaskTree. Para colocar un QTaskTree anidado dentro de otro QTaskTree, inserte el elemento QTaskTreeTask en otro elemento Group.

QTaskTree informa del progreso de las tareas completadas cuando se están ejecutando. El valor de progreso se incrementa cuando una tarea finaliza o es omitida o cancelada. Cuando QTaskTree finaliza y se emite la señal QTaskTree::done(), el valor actual del progreso es igual al valor máximo del progreso. El progreso máximo es igual al número total de tareas asíncronas en un árbol. Un QTaskTree anidado se cuenta como una única tarea, y sus tareas hijas no se cuentan en el árbol de nivel superior. Los grupos en sí no se cuentan como tareas, pero sí sus tareas. QSyncTask las tareas no son asíncronas, por lo que no se cuentan como tareas.

Para establecer datos iniciales adicionales para el árbol en ejecución, modifique las instancias de almacenamiento de un árbol cuando las cree instalando un gestor de configuración de almacenamiento:

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();

Cuando el árbol de tareas en ejecución crea una instancia CopyStorage, y antes de que se llame a cualquier manejador dentro de un árbol, el árbol de tareas llama al manejador initStorage, para permitir la configuración de datos iniciales del almacenamiento, únicos para esta ejecución particular de taskTree.

Del mismo modo, para recopilar algunos datos de resultados adicionales del árbol en ejecución, léalos de las instancias de almacenamiento del árbol cuando estén a punto de ser destruidas. Para ello, instale un manejador de almacenamiento realizado:

Storage<CopyStorage> storage;const Group root = ...; // almacenamiento colocado dentro del grupo de root y dentro de los manejadoresQTaskTree taskTree(root);auto collectStorage = [](const CopyStorage &storage) {    qDebug() << "final content" << storage.content;
}; taskTree.onStorageDone(storage, collectStorage); taskTree.start();

Cuando el árbol de tareas en ejecución está a punto de destruir una instancia de CopyStorage, el árbol de tareas llama al manejador collectStorage, para permitir la lectura de los datos finales del almacenamiento, únicos para esta ejecución particular de taskTree.

Adaptadores de tareas

Permitir que nuevos tipos de tareas formen parte de las recetas es bastante sencillo. Basta con definir un nuevo alias de tarea a la plantilla QCustomTask, pasando su tipo Task como primer argumento de la plantilla, como:

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

signals:
    void done(bool result);
};

using WorkerTask = QCustomTask<Worker>;

Esto funcionará, si se cumplen las siguientes condiciones:

  1. Tu tarea deriva de QObject.
  2. Tu tarea tiene un método público start() que inicia la tarea.
  3. Tu tarea emite la señal done(bool) o done(DoneResult) cuando termina.

Si tu tarea no cumple estas condiciones, aún puedes adaptarla para que funcione con el marco TaskTree, proporcionando un segundo argumento de plantilla con el adaptador personalizado. Digamos que queremos adaptar QTimer para que funcione con TaskTree. El Adapter podría tener el siguiente aspecto:

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>;

Ahora puedes empezar a utilizar TimerTask en tus recetas, como:

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

Nota: La clase que implementa la tarea en ejecución debe tener un constructor por defecto, y los objetos de esta clase deben ser libremente destructibles. Se debe permitir destruir una tarea en ejecución, preferiblemente sin esperar a que la tarea en ejecución termine (es decir, un destructor seguro no bloqueante de una tarea en ejecución). Para lograr una destrucción no bloqueante de una tarea que tiene un destructor bloqueante, considere el uso del parámetro de plantilla opcional Deleter del QCustomTask (el tercer argumento de plantilla).

Ejecutores de árbol de tareas

El ejecutor de árbol de tareas gestiona el tiempo de vida de la subyacente QTaskTree utilizada para ejecutar la receta dada.

La siguiente tabla resume las diferencias entre los distintos ejecutores de árbol de tareas:

Nombre de la claseDescripción
QSingleTaskTreeRunnerGestiona la ejecución de un único árbol de tareas. El método QSingleTaskTreeRunner::start() inicia incondicionalmente la receta pasada, reiniciando cualquier árbol de tareas que pudiera estar ejecutándose. Sólo se puede ejecutar un árbol de tareas a la vez.
QSequentialTaskTreeRunnerGestiona ejecuciones secuenciales de árboles de tareas. El método QSequentialTaskTreeRunner::enqueue() inicia la receta pasada si el ejecutor del árbol de tareas está inactivo. En caso contrario, la receta se pone en cola. Cuando la tarea actual finaliza, el ejecutor ejecuta la receta de la cola secuencialmente. Sólo se puede ejecutar un árbol de tareas a la vez.
QParallelTaskTreeRunnerGestiona ejecuciones paralelas de árboles de tareas. El método QParallelTaskTreeRunner::start() inicia incondicionalmente la receta pasada y mantiene en paralelo cualquier árbol de tareas que se esté ejecutando.
QMappedTaskTreeRunnerGestiona ejecuciones de árboles de tareas mapeados. El método QMappedTaskTreeRunner::start() inicia incondicionalmente la receta especificada para una clave dada. Si ya tiene un árbol de tareas diferente con la misma clave en ejecución, se reiniciará. Los árboles de tareas con claves diferentes no se ven afectados y continúan su ejecución.

© 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.