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
Engloba todas las clases y funciones globales del módulo TaskTree |
Clases
Un elemento body utilizado con las construcciones For y When | |
Un elemento "else" utilizado en expresiones condicionales | |
Elemento "else if" utilizado en expresiones condicionales | |
Clase base para elementos de tarea ejecutables | |
Elemento de grupo que describe el modo de ejecución | |
Un elemento de bucle for | |
Bucle infinito de subtareas | |
Iterador infinito que se utiliza dentro del elemento For | |
Representa el elemento básico para componer recetas declarativas que describen cómo ejecutar y manejar un árbol anidado de tareas asíncronas | |
Representa el elemento básico que puede formar parte de cualquier Grupo | |
Elemento "if" utilizado en expresiones condicionales | |
Clase base para ser utilizada como iterador dentro del elemento For | |
Iterador de lista a utilizar dentro del elemento For | |
Estructura que describe la subclase QObject y su señal | |
Modo de ejecución en paralelo con un límite personalizado | |
Una tarea asíncrona que finaliza bajo demanda | |
Una plantilla de clase utilizada para declarar elementos de tarea personalizados y definir sus manejadores de configuración y finalización | |
Una plantilla de clase que proporciona el adaptador de tarea por defecto utilizado en QCustomTask | |
Un controlador de ejecución de árbol de tareas mapeado con un tipo de clave dado | |
Una envoltura alrededor de QNetworkReply y QNetworkAccessManager | |
Un controlador de ejecución de árbol de tareas paralelo | |
Un eliminador personalizado para QProcess, utilizado por QProcessTask | |
Un controlador secuencial de la ejecución del árbol de tareas | |
Un controlador de ejecución de árbol de tareas simple | |
Un QBarrier iniciado con un límite dado | |
Ejecuta sincrónicamente un controlador personalizado entre otras tareas | |
Clase auxiliar utilizada al adaptar la interfaz de la tarea personalizada | |
Ejecuta el árbol de tareas asíncronas definidas de forma declarativa | |
Una envoltura alrededor de QTcpSocket | |
Una plantilla de clase que controla la ejecución de una función en un hilo separado a través de QtConcurrent::run() | |
Una clase base para la plantilla de clase QThreadFunction | |
Iterador repetitivo para ser utilizado dentro del elemento For | |
Una plantilla de clase para el intercambio de datos personalizados en el árbol de tareas en ejecución | |
Un elemento "then" utilizado en expresiones condicionales | |
Iterador condicional a utilizar dentro del elemento For | |
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:
- QTimer::singleShot()
- QProcess
- QNetworkAccessManager + QNetworkReply = QNetworkReplyWrapper
- QtConcurrent::run() + QFutureWatcher<Resultado> = QThreadFunction<Resultado>
Á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 |
|---|---|---|
| QProcessTask | QProcess | Inicia el proceso. |
QThreadFunctionTask<ReturnType> | QThreadFunction<ReturnType> | Inicia tarea asíncrona, se ejecuta en hilo separado. |
| QTaskTreeTask | QTaskTree | Inicia árbol de tareas anidadas. |
| QNetworkReplyWrapperTask | QNetworkReplyWrapper | Inicia la descarga de red. |
| QTcpSocketWrapperTask | QTcpSocketWrapper | Inicia 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 1 | Escenario 2 |
|---|---|
| Comienza el Grupo Raíz | Comienza el Grupo Raíz |
| Inicio del subgrupo | Inicio del subgrupo |
| QProcess inicia | QProcess inicia |
| QThreadFunction<int> starts | QThreadFunction<int> inicia |
| ... | ... |
| QProcess finaliza | QThreadFunction<int> finaliza |
| ... | ... |
| QThreadFunction<int> acabados | QProcess acabados |
| Sub Group finishes | Sub Group finaliza |
| QNetworkReplyWrapper inicia | QNetworkReplyWrapper inicia |
| ... | ... |
| QNetworkReplyWrapper finaliza | QNetworkReplyWrapper finaliza |
| Root Group finaliza | Root 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 Valor | Breve descripción |
|---|---|
| Continue | La 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). |
| StopWithSuccess | La tarea no se iniciará e informará de éxito a su padre. |
| StopWithError | La 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:
| Escenario | Comentario |
|---|---|
| Se inicia el grupo raíz | No devuelve SetupResult, por lo que se ejecutan sus tareas. |
| Se inicia el Grupo 1 | Devuelve 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 2 | Devuelve StopWithSuccess, por lo que el Proceso 2 se salta y el Grupo 2 informa de éxito. |
| Grupo 2 finaliza (éxito) | |
| Se inicia el grupo 3 | Devuelve 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:
- Definir la estructura personalizada
MyStoragecon datos personalizados [1], [2] - Crear una instancia del almacenamiento Storage<
MyStorage> [3] - Pasar la instancia Storage<
MyStorage> a los manejadores [4] - Acceder a la instancia
MyStorageen manejadores [5], [6] - 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:
- Tu tarea deriva de QObject.
- Tu tarea tiene un método público start() que inicia la tarea.
- 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 clase | Descripción |
|---|---|
| QSingleTaskTreeRunner | Gestiona 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. |
| QSequentialTaskTreeRunner | Gestiona 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. |
| QParallelTaskTreeRunner | Gestiona 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. |
| QMappedTaskTreeRunner | Gestiona 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.