이 페이지에서

Qt TaskTree C++ Classes

범용 TaskTree 라이브러리가 포함되어 있습니다. 더 보기...

이 모듈은 기술 미리보기 단계에 있으며, 향후 변경될 수 있습니다.

이 모듈은 Qt 6.11에서 처음 도입되었습니다.

네임스페이스

QtTaskTree

TaskTree 모듈의 모든 클래스와 전역 함수를 포함합니다.

클래스

QtTaskTree::Do

For 및 When 구문과 함께 사용되는 본문 요소

QtTaskTree::Else

조건식에서 사용되는 "else" 요소

QtTaskTree::ElseIf

조건식에서 사용되는 “else if” 요소

QtTaskTree::ExecutableItem

실행 가능한 작업 항목의 기본 클래스

QtTaskTree::ExecutionMode

실행 모드를 설명하는 그룹 요소

QtTaskTree::For

for 루프 요소

QtTaskTree::Forever

하위 작업의 무한 루프

QtTaskTree::ForeverIterator

For 요소 내부에서 사용되는 무한 반복자

QtTaskTree::Group

비동기 작업의 중첩된 트리를 실행하고 처리하는 방법을 설명하는 선언적 레시피를 구성하기 위한 기본 요소를 나타냅니다

QtTaskTree::GroupItem

어떤 그룹의 일부가 될 수 있는 기본 요소를 나타냅니다

QtTaskTree::If

조건식에서 사용되는 "if" 요소

QtTaskTree::Iterator

For 요소 내부에서 반복자로 사용되는 기본 클래스

QtTaskTree::ListIterator

For 요소 내에서 사용되는 리스트 이터레이터

QtTaskTree::ObjectSignal

QObject 서브클래스와 해당 시그널을 설명하는 구조체

QtTaskTree::ParallelLimit

사용자 정의 제한이 있는 병렬 실행 모드

QtTaskTree::QBarrier

요청 시 완료되는 비동기 작업

QtTaskTree::QCustomTask

사용자 정의 태스크 항목을 선언하고 해당 설정 및 완료 핸들러를 정의하는 데 사용되는 클래스 템플릿

QtTaskTree::QDefaultTaskAdapter

QCustomTask에서 사용되는 기본 태스크 어댑터를 제공하는 클래스 템플릿

QtTaskTree::QMappedTaskTreeRunner

주어진 Key 유형을 갖는 매핑된 태스크 트리 실행 컨트롤러

QtTaskTree::QNetworkReplyWrapper

QNetworkReply 및 QNetworkAccessManager를 감싸는 래퍼

QtTaskTree::QParallelTaskTreeRunner

병렬 태스크 트리 실행 컨트롤러

QtTaskTree::QProcessTaskDeleter

QProcessTask에서 사용하는 QProcess용 사용자 정의 삭제기

QtTaskTree::QSequentialTaskTreeRunner

순차적 태스크 트리 실행 컨트롤러

QtTaskTree::QSingleTaskTreeRunner

단일 태스크 트리 실행 컨트롤러

QtTaskTree::QStartedBarrier

지정된 제한을 가진 시작된 QBarrier

QtTaskTree::QSyncTask

다른 태스크들 사이에서 사용자 정의 핸들러를 동기식으로 실행합니다

QtTaskTree::QTaskInterface

사용자 정의 태스크의 인터페이스를 적용할 때 사용되는 헬퍼 클래스

QtTaskTree::QTaskTree

선언적 방식으로 정의된 비동기 태스크 트리를 실행합니다

QtTaskTree::QTcpSocketWrapper

QTcpSocket을 감싸는 래퍼

QtTaskTree::QThreadFunction

QtConcurrent::run()을 통해 별도의 스레드에서 함수 실행을 제어하는 클래스 템플릿

QtTaskTree::QThreadFunctionBase

QThreadFunction 클래스 템플릿의 기저 클래스

QtTaskTree::RepeatIterator

For element 내부에서 사용되는 반복 이터레이터

QtTaskTree::Storage

실행 중인 작업 트리 내에서 사용자 정의 데이터 교환을 위한 클래스 템플릿

QtTaskTree::Then

조건식에서 사용되는 "then" 요소

QtTaskTree::UntilIterator

For 요소 내부에서 사용되는 조건부 이터레이터

QtTaskTree::When

배리어 진행 시까지 본문의 실행을 지연시키는 요소

상세 설명

TaskTree 라이브러리를 사용하여 어떤 비동기 작업이 실행되어야 하는지를 기술하는 레시피를 구성하고, QTaskTree 내에서 이러한 레시피를 사용하여 작업을 실행합니다.

레시피는 생성 및 실행될 태스크 유형(예: QProcess, QNetworkReplyWrapper, QThreadFunction<ReturnType>)과, 태스크가 순차적으로 실행될지 병렬로 실행될지에 대한 선언적 설명입니다. 레시피 내에서는 이전 작업이 성공했는지 오류가 발생했는지에 따라 서로 다른 후속 처리 경로를 정의할 수 있습니다. 또한 ` Group ` 요소 안에 작업을 중첩할 수 있으며, 각 ` Group `는 자체 실행 모드나 워크플로 정책에 따라 작업을 실행할 수 있습니다. 이러한 레시피들이 작업 트리 구조를 형성합니다.

비동기 작업

비동기 작업이란 시작될 수 있으며, 나중에 성공 또는 오류로 완료되는 모든 작업을 말합니다. ‘나중에’란 작업을 시작한 후 제어권이 실행 중인 이벤트 루프로 돌아간다는 것을 의미합니다. 작업이 완료될 때까지 호출자 스레드를 차단하지 않습니다. 작업 트리를 사용하려면 이벤트 루프가 작동 중이어야 합니다.

비동기 작업의 예:

레시피 및 작업 트리

레시피와 태스크 트리가 무엇인지 기억하기 위해, 카트리지와 플레이어에 비유해 보겠습니다. 레시피를 작성하는 것은 마치 카트리지를 제작하는 것과 같으므로, 카트리지가 플레이어(태스크 트리)에 삽입되어 실행될 때 플레이어가 나중에 무엇을 해야 할지에 대한 상세한 설명을 준비하면 됩니다. 레시피 자체는 레시피가 태스크 트리로 전달되고 태스크 트리가 시작되었을 때, 태스크 트리가 무엇을 해야 하는지에 대한 선언적 설명일 뿐입니다. 레시피 자체는 태스크 트리 없이는 아무것도 하지 않습니다. 마치 플레이어가 없는 카트리지와 같습니다.

다음은 레시피와 태스크 트리의 역할에 대한 간략한 요약입니다.

레시피(카트리지)는 다음을 기술합니다:

  • 실행 중인 태스크 트리(QCustomTask를 통해)가 동적으로 생성해야 할 태스크가 무엇인지
  • 어떤 순서로 생성되어야 하는지
  • 실행 중인 작업 트리에서 ( Storage 를 통해) 동적으로 생성해야 할 데이터 구조는 무엇인지
  • 시작 전에 각 태스크를 어떻게 설정해야 하는지
  • 작업이 완료되었을 때 데이터를 수집하는 방법
  • 실행 모드(작업은 순차적으로 실행되어야 하는지, 병렬로 실행되어야 하는지)
  • 워크플로 정책

작업 트리 (플레이어):

  • 레시피를 읽어들이고 태스크 및 데이터 구조를 자동으로 생성합니다
  • 생성된 작업 및 데이터 구조의 수명 주기를 관리합니다
  • 연속 작업을 실행합니다
  • 완료된 작업의 결과와 워크플로 정책에 따라 다양한 경로를 선택합니다
  • 기본적인 진행 상황을 제공합니다

사용자 정의 작업

레시피는 태스크 트리가 시작될 때 생성해야 할 태스크에 대한 설명이므로, 레시피 내부에서 이러한 태스크를 직접 생성할 수는 없습니다. 대신, 나중에 태스크 트리가 우리를 대신하여 이러한 태스크를 생성하고 시작하도록 지시하는 선언적인 방식이 필요합니다. 예를 들어, 태스크 트리에서 QProcess 을 생성하고 시작하도록 하려면, 레시피 내에 QProcessTask 요소를 배치하여 이를 정의합니다. QProcessTask 은 QCustomTask<QProcess>의 별칭입니다. 각 태스크 유형은 레시피 내에서 사용될 수 있도록 해당 QCustomTask<Type>을 제공해야 합니다.

다음 표는 레시피 내에 바로 배치할 수 있는 몇 가지 내장 사용자 정의 태스크를 보여줍니다:

사용자 정의 태스크 (레시피에서 사용)태스크 클래스 (실행 중인 태스크 트리에 의해 생성됨)간략한 설명
QProcessTaskQProcess프로세스를 시작합니다.
QThreadFunctionTask<ReturnType>QThreadFunction<ReturnType>비동기 태스크를 시작하며, 별도의 스레드에서 실행됩니다.
QTaskTreeTaskQTaskTree중첩된 작업 트리를 시작합니다.
QNetworkReplyWrapperTaskQNetworkReplyWrapper네트워크 다운로드를 시작합니다.
QTcpSocketWrapperTaskQTcpSocketWrapperTCP 연결을 시작합니다.

레시피 내에서 특정 태스크를 사용하도록 조정하는 방법에 대한 자세한 내용은 QTaskInterface 및 Task Adapters 을 참조하십시오.

레시피 예시

QTaskTree 파일에는 최상위 Group 요소(일명 레시피)가 있으며, 여기에는 QProcessTask, QNetworkReplyWrapperTask, QThreadFunctionTask<ReturnType>와 같은 다양한 유형의 태스크를 원하는 수만큼 포함할 수 있습니다:

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

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

위의 레시피는 Group 유형의 최상위 요소로 구성되어 있으며, 이 요소에는 QProcessTask, QNetworkReplyWrapperTask, QThreadFunctionTask<int> 유형의 태스크가 포함되어 있습니다. taskTree->start() 가 호출되면, QProcess 을 시작으로 태스크들이 생성되어 체인 형태로 실행됩니다. QProcess 가 성공적으로 완료되면 QNetworkReplyWrapper 태스크가 시작됩니다. 마지막으로, 네트워크 태스크가 성공적으로 완료되면 QThreadFunction<int> 태스크가 시작됩니다.

마지막으로 실행 중인 작업이 성공적으로 완료되면, 작업 트리가 성공적으로 실행된 것으로 간주되며, DoneWith::Success 를 사용하여 QTaskTree::done() 신호가 발생합니다. 작업이 오류로 종료되면 작업 트리의 실행이 중지되고 나머지 작업들은 건너뜁니다. 작업 트리는 오류로 종료되며, DoneWith::Error 를 사용하여 QTaskTree::done() 신호를 보냅니다.

그룹

Group 의 상위 태스크는 이를 단일 태스크로 인식합니다. 다른 태스크와 마찬가지로, 그룹도 시작될 수 있으며 성공 또는 오류로 종료될 수 있습니다. Group 요소는 중첩되어 트리 구조를 형성할 수 있습니다:

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

위의 예제는 최상위 요소에 ` QProcessTask `과 ` QThreadFunctionTask<int>`을 포함하는 하위 그룹이 있다는 점에서 첫 번째 예제와 다릅니다. 이 하위 그룹은 루트에 있는 ` QNetworkReplyWrapperTask `의 형제 요소입니다. 하위 그룹에는 ` Group `에 작업을 병렬로 실행하도록 지시하는 추가적인 ` parallel ` 요소가 포함되어 있습니다.

따라서 QTaskTree 가 위의 레시피를 시작하면, QProcess 와 QThreadFunction<int>가 즉시 시작되어 병렬로 실행됩니다. 루트 그룹에는 parallel 요소가 포함되어 있지 않으므로, 그 직접 자식 태스크들은 순차적으로 실행됩니다. 따라서 QNetworkReplyWrapper 는 전체 하위 그룹이 완료된 후에 시작됩니다. 그룹은 모든 태스크가 완료되었을 때 완료된 것으로 간주됩니다. 태스크가 완료되는 순서는 중요하지 않습니다.

따라서 어떤 작업이 더 오래 걸리는지에 따라(QProcess 또는 QThreadFunction<int>), 다음과 같은 시나리오가 발생할 수 있습니다:

시나리오 1시나리오 2
루트 그룹 시작루트 그룹이 시작됨
하위 그룹 시작하위 그룹 시작
QProcess 시작QProcess 시작
QThreadFunction<int> 시작QThreadFunction<int> 시작
......
QProcess 종료QThreadFunction<int>가 종료됨
......
QThreadFunction<int> finishesQProcess 완료
하위 그룹이 완료됨하위 그룹 완료
QNetworkReplyWrapper 시작QNetworkReplyWrapper 시작
......
QNetworkReplyWrapper 완료QNetworkReplyWrapper가 종료됩니다
루트 그룹이 완료됨루트 그룹이 완료됨

각 시나리오 간의 차이점은 굵은 글씨로 표시되어 있습니다. 점 세 개는 이전 이벤트와 다음 이벤트 사이에 지정되지 않은 시간이 경과함을 의미합니다(하나 이상의 작업이 계속 실행 중임). 이벤트 사이에 점이 없는 경우, 이벤트들이 동기적으로 발생함을 의미합니다.

제시된 시나리오는 모든 태스크가 성공적으로 실행된다는 전제를 바탕으로 합니다. 실행 중에 태스크가 실패하면 태스크 트리는 오류와 함께 종료됩니다. 특히, QProcess 가 오류와 함께 종료되는 동안 QThreadFunction<int>가 아직 실행 중일 경우, QThreadFunction<int>는 자동으로 취소되고, 하위 그룹은 오류와 함께 종료되며, QNetworkReplyWrapper 는 건너뛰어지고, 트리는 오류와 함께 종료됩니다.

작업 핸들러

Task 핸들러를 사용하여 실행할 태스크를 설정하고, 태스크가 성공 또는 오류로 종료되었을 때 해당 태스크의 출력 데이터를 읽을 수 있도록 합니다.

작업의 시작 핸들러

태스크 객체가 생성되고 실행되기 전에, 태스크 트리는 사용자가 선택적으로 제공한 설정 핸들러를 호출합니다. 설정 핸들러는 항상 관련 태스크 클래스 객체에 대한 참조를 받아야 합니다:

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

설정 핸들러에서 전달된 ` QProcess `를 수정하여, 태스크 트리가 사용자의 구성에 따라 프로세스를 시작할 수 있도록 할 수 있습니다. 설정 핸들러 내에서 ` process.start(); `를 호출해서는 안 됩니다. 태스크 트리가 필요할 때 이를 호출하기 때문입니다. 설정 핸들러는 선택 사항입니다. 사용될 경우, 반드시 태스크 생성자의 첫 번째 인수로 지정되어야 합니다.

선택적으로, 설정 핸들러는 ` SetupResult`를 반환할 수 있습니다. 반환된 ` SetupResult `는 해당 태스크의 향후 시작 동작에 영향을 미칩니다. 가능한 값은 다음과 같습니다:

SetupResult 값간략한 설명
Continue태스크가 정상적으로 시작됩니다. 이는 설정 핸들러가 ` SetupResult `를 반환하지 않을 때(즉, 반환 유형이 ` void`인 경우)의 기본 동작입니다.
StopWithSuccess태스크가 시작되지 않으며, 상위 태스크에 성공을 보고합니다.
StopWithError작업이 시작되지 않으며 상위 작업에 오류를 보고합니다.

이는 특정 조건이 충족될 때만 태스크를 실행해야 하고, 이 조건을 평가하는 데 필요한 데이터가 이전에 시작된 태스크가 완료될 때까지 알 수 없는 경우에 유용합니다. 이러한 방식으로, 설정 핸들러는 해당 태스크를 정상적으로 시작할지, 아니면 건너뛰고 성공 또는 오류를 보고할지 동적으로 결정합니다. 태스크 간 데이터 교환에 대한 자세한 내용은 Storage 을 참조하십시오.

작업의 완료 핸들러

실행 중인 작업이 완료되면, 작업 트리는 선택적으로 제공된 완료 핸들러를 호출합니다. 이 핸들러는 관련 작업 클래스 객체에 대한 const 참조를 받아야 합니다:

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();
   else
        qDebug() << "Failure" << process.cleanedStdErr();
};
const Group root {
    QProcessTask(onSetup, onDone)
};

done 핸들러는 QProcess 에서 출력 데이터를 수집하여 후속 처리를 위해 저장하거나 추가 작업을 수행할 수 있습니다.

참고: 작업 설정 핸들러가 ` StopWithSuccess ` 또는 ` StopWithError`를반환하면 , 완료 핸들러는 호출되지 않습니다.

그룹 핸들러

작업 핸들러와 마찬가지로, 그룹 핸들러를 사용하면 그룹을 구성하여 실행하고, 그룹 전체가 성공 또는 오류로 완료되었을 때 추가 작업을 적용할 수 있습니다.

그룹의 시작 핸들러

작업 트리는 자식 작업을 시작하기 전에 그룹 시작 핸들러를 호출합니다. 그룹 핸들러는 인수를 받지 않습니다:

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

그룹 설정 핸들러는 선택 사항입니다. 그룹 설정 핸들러를 정의하려면 그룹에 ` onGroupSetup()` 요소를 추가하십시오. ` onGroupSetup()`의 인수는 사용자 정의 핸들러입니다. 그룹에 ` onGroupSetup()` 요소를 두 개 이상 추가하면 런타임 시 오류 메시지가 포함된 어서트(assert)가 발생합니다.

작업의 시작 핸들러와 마찬가지로, 그룹 시작 핸들러는 ` SetupResult`를 반환할 수 있습니다. 반환된 ` SetupResult ` 값은 전체 그룹의 시작 동작에 영향을 미칩니다. 그룹 시작 핸들러를 지정하지 않거나 반환 유형이 `void`인 경우, 그룹의 기본 동작은 ` Continue`이므로 모든 작업이 정상적으로 시작됩니다. 반면, 시작 핸들러가 ` StopWithSuccess ` 또는 ` StopWithError`를 반환하면 태스크는 시작되지 않고(건너뜁니다) 그룹 자체는 반환된 값에 따라 각각 성공 또는 오류를 보고합니다.

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

위의 예제에서는 루트 그룹의 모든 하위 그룹이 각자의 설정 핸들러를 정의합니다. 다음 시나리오는 시작된 모든 프로세스가 성공적으로 종료된다고 가정합니다:

시나리오설명
루트 그룹 시작SetupResult를 반환하지 않으므로 해당 작업이 실행됩니다.
그룹 1이 시작됩니다Continue를 반환하므로 해당 작업이 실행됩니다.
프로세스 1이 시작됩니다
......
프로세스 1이 종료됩니다 (성공)
그룹 1이 종료됩니다(성공)
그룹 2가 시작됩니다StopWithSuccess가 반환되므로 프로세스 2는 건너뛰어지고 그룹 2는 성공을 보고합니다.
그룹 2가 완료됨 (성공)
그룹 3이 시작됩니다StopWithError를 반환하므로 프로세스 3은 건너뛰고 그룹 3은 오류를 보고합니다.
그룹 3이 완료됩니다(오류).
루트 그룹이 완료됨(오류)루트 그룹의 직접 하위 그룹인 그룹 3이 오류로 종료되었으므로, 루트 그룹은 실행을 중지하고 아직 시작되지 않은 프로세스 4를 건너뛰고 오류를 보고합니다.

그룹의 Done 핸들러

Group 의 완료 핸들러는 해당 그룹의 작업이 성공적으로 또는 실패로 종료된 후에 실행됩니다. 그룹이 보고하는 최종 값은 그룹의 ` Workflow Policy`에 따라 달라집니다. 핸들러는 기타 필요한 조치를 수행할 수 있습니다. 완료 핸들러는 그룹의 ` onGroupDone()` 요소 내부에 정의됩니다. 이 핸들러는 선택적 인자 ` DoneWith `를 받을 수 있으며, 이 인자는 실행의 성공 또는 실패 여부를 나타냅니다:

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

그룹 완료 핸들러는 선택 사항입니다. 한 그룹에 onGroupDone()을 두 개 이상 추가하면 런타임에 오류 메시지가 포함된 어설트가 발생합니다.

참고: 그룹 설정 핸들러가 StopWithSuccess 또는 StopWithError 를반환하더라도 그룹의 완료 핸들러는 호출됩니다. 이 동작은 작업 완료 핸들러의 동작과 다르며, 향후 변경될 수 있습니다.

기타 그룹 요소

그룹에는 execution mode 이나 workflow policy 와 같이 처리 흐름을 설명하는 다른 요소들이 포함될 수 있습니다. 또한 그룹 실행 중에 수집된 사용자 정의 공통 데이터를 수집하고 공유하는 역할을 하는 저장소 요소도 포함될 수 있습니다.

실행 모드

그룹 내의 실행 모드(execution mode) 요소는 그룹의 직접 하위 작업이 어떻게 시작되는지를 지정합니다. 가장 일반적인 실행 모드는 sequential 및 parallel 입니다. 또한 ParallelLimit 요소를 사용하여 병렬로 실행되는 작업의 상한을 지정할 수도 있습니다.

모든 실행 모드에서 그룹은 작업이 나타나는 순서대로 작업을 시작합니다.

그룹의 자식 태스크가 또한 그룹인 경우, 해당 자식 그룹은 자체 실행 모드에 따라 태스크를 실행합니다.

워크플로우 정책

Group 의 워크플로 정책 요소는 그룹의 직접 하위 그룹 중 하나의 작업이 완료되었을 때 그룹이 어떻게 동작해야 하는지를 지정합니다. 적용 가능한 정책에 대한 자세한 설명은 WorkflowPolicy 을 참조하십시오.

그룹의 자식 요소가 또한 그룹인 경우, 해당 자식 그룹은 자체 워크플로 정책에 따라 작업을 실행합니다.

저장소

Storage 요소를 사용하여 태스크 간에 정보를 교환할 수 있습니다. 특히 순차 실행 모드에서, 한 태스크가 시작되기 전에 이미 완료된 다른 태스크의 데이터가 필요한 경우에 유용합니다. 예를 들어, 소스에서 데이터를 읽어 목적지에 쓰면서 데이터를 복사하는 태스크 트리는 다음과 같이 보일 수 있습니다:

static 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] 사용자 정의 태스크 간 구조체
        QByteArray content; // [2] 사용자 정의 태스크  간  데이터
    };

    // [3] 태스크 트리에서 관리 가능한 사용자 정의 태스크 간 구조체의 인스턴스
    const Storage<CopyStorage> storage;

    const auto onLoaderSetup = [source](QThreadFunction<QByteArray> &async) {
        async.setThreadFunctionData(&load, source);
    };
   // [4] 런타임: 태스크 트리는 핸들러를 호출하기 전에 [7]의 인스턴스를 활성화합니다
    const auto onLoaderDone = [storage](const QThreadFunction<QByteArray> &async) {
        storage->content = async.result(); // [5] 로더가 결과를 스토리지에 저장합니다
    };

   // [4] 런타임: 태스크 트리가 핸들러를 호출하기 전에 [7]의 인스턴스를 활성화합니다
    const auto onSaverSetup = [storage, destination](QThreadFunction<void> &async) {
        const QByteArray content = storage->content; // [6] 세이브러가 스토리지에서 데이터를 가져옴
        async.setThreadFunctionData(&save, destination, content);
    };
    const auto onSaverDone = [](const QThreadFunction<void> &async) {
        qDebug() << "Save done successfully";
    };

    const Group root {
        // [7] 런타임: 루트에 진입할 때 작업 트리가 CopyStorage 인스턴스를 생성함
        storage,
        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();

위의 예제에서, 태스크 간 데이터는 CopyStorage 사용자 정의 구조체 [1]로 감싸진 QByteArray 콘텐츠 변수 [2]로 구성됩니다. 로더가 성공적으로 완료되면, 데이터를 CopyStorage::content 변수 [5]에 저장합니다. 그런 다음 세이버는 이 변수를 사용하여 저장 태스크를 구성합니다 [6].

태스크 트리가 CopyStorage 구조체를 관리할 수 있도록 하기 위해, Storage<CopyStorage>의 인스턴스가 생성됩니다 [3]. 이 객체의 복사본이 그룹의 하위 항목으로 삽입되면 [7], 작업 트리가 이 그룹에 진입할 때 CopyStorage 구조체의 인스턴스가 동적으로 생성됩니다. 작업 트리가 이 그룹을 벗어나면, 더 이상 필요하지 않으므로 CopyStorage 구조체의 기존 인스턴스는 소멸됩니다.

공통 Storage<CopyStorage> 인스턴스의 복사본을 보유한 여러 태스크 트리가 동시에 실행되는 경우(태스크 트리가 서로 다른 스레드에서 실행되는 경우 포함), 각 태스크 트리는 자체적인 CopyStorage 구조체 복사본을 포함합니다.

저장소 객체를 가진 그룹 내의 모든 핸들러에서 CopyStorage 에 접근할 수 있습니다. 여기에는 저장소 객체를 가진 그룹의 모든 하위 태스크의 모든 핸들러가 포함됩니다. 핸들러에서 사용자 정의 구조체에 접근하려면, Storage<CopyStorage> 객체의 복사본을 핸들러에 전달하십시오(예: 람다 캡처에서) [4].

태스크 트리가 스토리지[7]를 포함하는 하위 트리의 핸들러를 호출할 때, 태스크 트리는 Storage<CopyStorage> 객체 내부에서 자체 CopyStorage 인스턴스를 활성화합니다. 따라서 CopyStorage 구조체는 핸들러 본문 내에서만 액세스할 수 있습니다. Storage<CopyStorage> 내에서 현재 활성화된 CopyStorage 에 액세스하려면 Storage::operator->(), Storage::operator*() 또는 Storage::activeStorage() 메서드를 사용하십시오.

다음 목록은 작업 트리에서 Storage 객체를 활용하는 방법을 요약한 것입니다:

  1. 사용자 정의 데이터 [1], [2]를 사용하여 사용자 정의 구조체 MyStorage 를 정의합니다.
  2. Storage<MyStorage> 스토리지의 인스턴스를 생성합니다 [3]
  3. Storage<MyStorage> 인스턴스를 핸들러에 전달합니다 [4]
  4. 핸들러에서 MyStorage 인스턴스에 접근 [5], [6]
  5. Storage<MyStorage> 인스턴스를 그룹에 삽입 [7]

QTaskTree 클래스

QTaskTree Group 루트 요소가 정의한 순서에 따라 비동기 태스크의 트리 구조를 실행합니다.

QTaskTree 역시 비동기 태스크이므로, 다른 QTaskTree 의 일부가 될 수 있습니다. 중첩된 QTaskTree 를 다른 QTaskTree 내에 배치하려면, QTaskTreeTask 요소를 다른 Group 요소 안에 삽입하십시오.

QTaskTree 실행 중 완료된 작업의 진행 상황을 보고합니다. 작업이 완료되거나 건너뛰어지거나 취소되면 진행 상황 값이 증가합니다. QTaskTree 이 완료되고 QTaskTree::done() 신호가 발산되면, 진행 상황의 현재 값은 최대 진행 상황 값과 같아집니다. 최대 진행 상황 값은 트리 내의 비동기 작업 총수와 같습니다. 중첩된 ` QTaskTree `는 단일 작업으로 간주되며, 그 자식 작업들은 최상위 트리에서 집계되지 않습니다. 그룹 자체는 작업으로 집계되지 않지만, 그룹 내의 작업들은 집계됩니다. ` QSyncTask ` 작업은 비동기적이지 않으므로 작업으로 집계되지 않습니다.

실행 중인 트리에 대한 추가 초기 데이터를 설정하려면, 스토리지 설정 핸들러를 설치하여 트리가 스토리지 인스턴스를 생성할 때 이를 수정하십시오:

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

실행 중인 작업 트리가 ` CopyStorage ` 인스턴스를 생성할 때, 트리 내부의 어떤 핸들러도 호출되기 전에 작업 트리는 ` initStorage ` 핸들러를 호출하여, 해당 ` taskTree` 실행에 고유한 스토리지의 초기 데이터를 설정할 수 있도록 합니다.

마찬가지로, 실행 중인 트리에서 추가 결과 데이터를 수집하려면, 트리 내의 스토리지 인스턴스가 소멸되기 직전에 해당 인스턴스에서 데이터를 읽어야 합니다. 이를 위해 스토리지 완료 핸들러를 설치하십시오:

Storage<CopyStorage> storage;
const Group root =...; // 루트 그룹 내부 및 핸들러 내에 위치한 스토리지
QTaskTree taskTree(root);
auto collectStorage = [](const CopyStorage &storage) {
    qDebug() << "final content" << storage.content;
};
taskTree.onStorageDone(storage, collectStorage);
taskTree.start();

실행 중인 태스크 트리가 CopyStorage 인스턴스를 파기하려 할 때, 태스크 트리는 collectStorage 핸들러를 호출하여 taskTree 의 이번 특정 실행에 고유한 최종 데이터를 스토리지에서 읽을 수 있도록 합니다.

태스크 어댑터

레시피에 새로운 Task 유형을 포함시키는 것은 매우 쉽습니다. 다음과 같이 Task 유형을 첫 번째 템플릿 인수로 전달하여 QCustomTask 템플릿에 대한 새로운 태스크 별칭을 정의하기만 하면 됩니다.

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

signals:
    void done(bool result);
};

using WorkerTask = QCustomTask<Worker>;

다음 조건이 충족되면 이 코드가 정상적으로 작동합니다:

  1. 태스크가 ` QObject`에서 파생된 것이어야 합니다.
  2. 태스크에 태스크를 시작하는 public start() 메서드가 있어야 합니다.
  3. 태스크가 완료되면 done(bool) 또는 done(DoneResult) 신호를 발생시켜야 합니다.

태스크가 이러한 조건을 충족하지 않더라도, 사용자 정의 어댑터를 두 번째 템플릿 인자로 제공함으로써 TaskTree 프레임워크와 연동되도록 태스크를 조정할 수 있습니다. 예를 들어, QTimer 를 TaskTree와 연동되도록 조정하고자 한다고 가정해 봅시다. 이 경우 Adapter 는 다음과 같이 작성될 수 있습니다:

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

이제 레시피에서 다음과 같이 TimerTask 를 사용할 수 있습니다:

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

참고: 실행 중인 태스크를 구현하는클래스에는 기본 생성자가 있어야 하며, 이 클래스의 객체는 자유롭게 소멸될 수 있어야 합니다. 실행 중인 태스크가 완료될 때까지 기다리지 않고도(즉, 실행 중인 태스크의 안전하고 비차단적인 소멸자) 해당 태스크를 소멸시킬 수 있어야 합니다. 블로킹 소멸자를 가진 태스크를 비차단 방식으로 소멸시키려면, ` QCustomTask `의 선택적 템플릿 매개변수인 ` Deleter `(세 번째 템플릿 인자)를 사용하는 것을 고려해 보십시오.

태스크 트리 러너

태스크 트리 러너는 주어진 레시피를 실행하는 데 사용되는 기본 QTaskTree 의 수명을 관리합니다.

다음 표는 다양한 태스크 트리 실행기 간의 차이점을 요약한 것입니다:

클래스 이름설명
QSingleTaskTreeRunner단일 태스크 트리 실행을 관리합니다. ` QSingleTaskTreeRunner::start()` 메서드는 전달된 레시피를 무조건 시작하며, 실행 중일 수 있는 모든 태스크 트리를 재설정합니다. 한 번에 하나의 태스크 트리만 실행될 수 있습니다.
QSequentialTaskTreeRunner순차적인 태스크 트리 실행을 관리합니다. ` QSequentialTaskTreeRunner::enqueue()` 메서드는 태스크 트리 러너가 유휴 상태일 때 전달된 레시피를 시작합니다. 그렇지 않은 경우, 레시피는 큐에 추가됩니다. 현재 태스크가 완료되면 러너는 큐에서 제거된 레시피를 순차적으로 실행합니다. 한 번에 하나의 태스크 트리만 실행될 수 있습니다.
QParallelTaskTreeRunner병렬 태스크 트리 실행을 관리합니다. ` QParallelTaskTreeRunner::start()` 메서드는 전달된 레시피를 무조건 시작하며, 실행 중인 태스크 트리가 있다면 이를 병렬로 유지합니다.
QMappedTaskTreeRunner매핑된 태스크 트리 실행을 관리합니다. ` QMappedTaskTreeRunner::start()` 메서드는 지정된 키에 대한 레시피를 무조건 시작하며, 이미 실행 중인 다른 태스크 트리가 있다면 이를 재설정합니다. 키가 다른 태스크 트리는 영향을 받지 않고 계속 실행됩니다.

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