다음을 통해 공유


방법: 지연 후 완료되는 작업 만들기

이 예제는 concurrency::task, concurrency::cancellation_token_source, concurrency::cancellation_token, concurrency::task_completion_event, concurrency::timer, 및 concurrency::call 클래스를 사용하여 지연 후 완료되는 작업을 만드는 방법을 보여줍니다. 이 메서드를 사용하여 때때로 데이터를 폴링하는 루프를 빌드하고, 제한 시간을 소개하고, 미리 지정된 시간 동안에 사용자의 입력 처리를 지연하는 데 사용할 수 있습니다.

예제

다음 예제에서는 complete_aftercancel_after_timeout 함수를 보여 줍니다. complete_after 함수는 지정 된 지연 시간 후에 완료된 task 개체를 생성합니다. timer 개체와 call 개체를 사용하여 지정된 지연 시간 후에 task_completion_event 개체를 설정합니다. task_completion_event 클래스를 사용하여, 스레드 또는 다른 작업이 값을 사용할 수 있는 신호를 보낸 후에 완성된 작업을 정의할 수 있습니다. 이벤트가 설정되었으면 수신기 작업이 완료되고 해당 연속 작업의 실행이 예약됩니다.

비동기 에이전트 라이브러리의 일부로 참조하는 timercall 클래스에 대한 더 자세한 내용은 비동기 메시지 블록를 참조하십시오.

cancel_after_timeout 함수는 complete_after 함수를 빌드하여, 주어진된 제한 시간 전에 작업이 완료 되지 않으면 작업을 취소합니다. cancel_after_timeout 함수는 두 작업을 만듭니다. 첫 번째 작업은 성공을 나타내고 제공 작업을 완료 한 후 완료됩니다. 두 번째 작업이 실패했음을 보여주고 지정된 시간 후에 완료됩니다. cancel_after_timeout 함수는 성공 또는 실패 작업 완료 시 실행 되는 연속 작업을 만듭니다. 실패의 미션을 완료 한 최초의 연속 토큰 소스는 전체 작업을 취소하려면 취소합니다.

// Creates a task that completes after the specified delay.
task<void> complete_after(unsigned int timeout)
{
    // A task completion event that is set when a timer fires.
    task_completion_event<void> tce;

    // Create a non-repeating timer.
    auto fire_once = new timer<int>(timeout, 0, nullptr, false);
    // Create a call object that sets the completion event after the timer fires.
    auto callback = new call<int>([tce](int)
    {
        tce.set();
    });

    // Connect the timer to the callback and start the timer.
    fire_once->link_target(callback);
    fire_once->start();

    // Create a task that completes after the completion event is set.
    task<void> event_set(tce);

    // Create a continuation task that cleans up resources and 
    // and return that continuation task. 
    return event_set.then([callback, fire_once]()
    {
        delete callback;
        delete fire_once;
    });
}

// Cancels the provided task after the specifed delay, if the task 
// did not complete. 
template<typename T>
task<T> cancel_after_timeout(task<T> t, cancellation_token_source cts, unsigned int timeout)
{
    // Create a task that returns true after the specified task completes.
    task<bool> success_task = t.then([](T)
    {
        return true;
    });
    // Create a task that returns false after the specified timeout.
    task<bool> failure_task = complete_after(timeout).then([]
    {
        return false;
    });

    // Create a continuation task that cancels the overall task  
    // if the timeout task finishes first. 
    return (failure_task || success_task).then([t, cts](bool success)
    {
        if(!success)
        {
            // Set the cancellation token. The task that is passed as the 
            // t parameter should respond to the cancellation and stop 
            // as soon as it can.
            cts.cancel();
        }

        // Return the original task. 
        return t;
    });
}

다음 예제에서는 배열에서 소수의 수를 [0, 100000] 범위에서 여러 번 계산합니다. 주어진 시간 안에 완료되지 않으면 작업이 실패합니다. count_primes 함수는 cancel_after_timeout 함수를 사용하는 방법을 보여줍니다. 지정된 범위 내에서 primes의 개수를 계산하고 제공된 시간에 작업이 완료 되지 않으면 실패 합니다. wmain 함수는 count_primes 함수를 여러 번 호출합니다. 될 때마다 제한 시간은 절반이 됩니다. 프로그램은 현재 제한된 시간안에 작업이 완료되지 않은 후에 종료됩니다.

// Determines whether the input value is prime. 
bool is_prime(int n)
{
    if (n < 2)
        return false;
    for (int i = 2; i < n; ++i)
    {
        if ((n % i) == 0)
            return false;
    }
    return true;
}

// Counts the number of primes in the range [0, max_value]. 
// The operation fails if it exceeds the specified timeout. 
bool count_primes(unsigned int max_value, unsigned int timeout)
{
    cancellation_token_source cts;

    // Create a task that computes the count of prime numbers. 
    // The task is canceled after the specified timeout.
    auto t = cancel_after_timeout(task<size_t>([max_value, timeout]
    {
        combinable<size_t> counts;
        parallel_for<unsigned int>(0, max_value + 1, [&counts](unsigned int n) 
        {
            // Respond if the overall task is cancelled by canceling  
            // the current task. 
            if (is_task_cancellation_requested())
            {
                cancel_current_task();
            }
            // NOTE: You can replace the calls to is_task_cancellation_requested 
            // and cancel_current_task with a call to interruption_point. 
            // interruption_point(); 

            // Increment the local counter if the value is prime. 
            if (is_prime(n))
            {
                counts.local()++;
            }
        });
        // Return the sum of counts across all threads. 
        return counts.combine(plus<size_t>());
    }, cts.get_token()), cts, timeout);

    // Print the result. 
    try
    {
        auto primes = t.get();
        wcout << L"Found " << primes << L" prime numbers within " 
              << timeout << L" ms." << endl;
        return true;
    }
    catch (const task_canceled& e)
    {
        wcout << L"The task timed out." << endl;
        return false;
    }
}

int wmain()
{
    // Compute the count of prime numbers in the range [0, 100000]  
    // until the operation fails. 
    // Each time the test succeeds, the time limit is halved. 

    unsigned int max = 100000;
    unsigned int timeout = 5000;

    bool success = true;
    do
    {
        success = count_primes(max, timeout);
        timeout /= 2;
    } while (success);
}
/* Sample output:
    Found 9592 prime numbers within 5000 ms.
    Found 9592 prime numbers within 2500 ms.
    Found 9592 prime numbers within 1250 ms.
    Found 9592 prime numbers within 625 ms.
    The task timed out.
*/

지연 후 작업을 취소하고 이 기술을 사용하면 전체 작업이 취소 된 후 어떤 시작되지 않은 작업이 시작되지 않습니다. 그러나, 어떤 장기 실행 작업이 적시에 취소에 응답하는 것이 중요합니다. 이 예제에서는 count_primes 메서드가 concurrency::is_task_cancellation_requestedcancel_current_task 함수를 호출해 취소에 응답합니다. (대신해서, concurrency::interruption_point 함수를 호출할 수 있습니다). 작업 취소에 대한 자세한 내용은 PPL에서의 취소를 참조하십시오.

이 예제의 전체 코드는 다음과 같습니다.

// task-delay.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

// Creates a task that completes after the specified delay.
task<void> complete_after(unsigned int timeout)
{
    // A task completion event that is set when a timer fires.
    task_completion_event<void> tce;

    // Create a non-repeating timer.
    auto fire_once = new timer<int>(timeout, 0, nullptr, false);
    // Create a call object that sets the completion event after the timer fires.
    auto callback = new call<int>([tce](int)
    {
        tce.set();
    });

    // Connect the timer to the callback and start the timer.
    fire_once->link_target(callback);
    fire_once->start();

    // Create a task that completes after the completion event is set.
    task<void> event_set(tce);

    // Create a continuation task that cleans up resources and 
    // and return that continuation task. 
    return event_set.then([callback, fire_once]()
    {
        delete callback;
        delete fire_once;
    });
}

// Cancels the provided task after the specifed delay, if the task 
// did not complete. 
template<typename T>
task<T> cancel_after_timeout(task<T> t, cancellation_token_source cts, unsigned int timeout)
{
    // Create a task that returns true after the specified task completes.
    task<bool> success_task = t.then([](T)
    {
        return true;
    });
    // Create a task that returns false after the specified timeout.
    task<bool> failure_task = complete_after(timeout).then([]
    {
        return false;
    });

    // Create a continuation task that cancels the overall task  
    // if the timeout task finishes first. 
    return (failure_task || success_task).then([t, cts](bool success)
    {
        if(!success)
        {
            // Set the cancellation token. The task that is passed as the 
            // t parameter should respond to the cancellation and stop 
            // as soon as it can.
            cts.cancel();
        }

        // Return the original task. 
        return t;
    });
}

// Determines whether the input value is prime. 
bool is_prime(int n)
{
    if (n < 2)
        return false;
    for (int i = 2; i < n; ++i)
    {
        if ((n % i) == 0)
            return false;
    }
    return true;
}

// Counts the number of primes in the range [0, max_value]. 
// The operation fails if it exceeds the specified timeout. 
bool count_primes(unsigned int max_value, unsigned int timeout)
{
    cancellation_token_source cts;

    // Create a task that computes the count of prime numbers. 
    // The task is canceled after the specified timeout.
    auto t = cancel_after_timeout(task<size_t>([max_value, timeout]
    {
        combinable<size_t> counts;
        parallel_for<unsigned int>(0, max_value + 1, [&counts](unsigned int n) 
        {
            // Respond if the overall task is cancelled by canceling  
            // the current task. 
            if (is_task_cancellation_requested())
            {
                cancel_current_task();
            }
            // NOTE: You can replace the calls to is_task_cancellation_requested 
            // and cancel_current_task with a call to interruption_point. 
            // interruption_point(); 

            // Increment the local counter if the value is prime. 
            if (is_prime(n))
            {
                counts.local()++;
            }
        });
        // Return the sum of counts across all threads. 
        return counts.combine(plus<size_t>());
    }, cts.get_token()), cts, timeout);

    // Print the result. 
    try
    {
        auto primes = t.get();
        wcout << L"Found " << primes << L" prime numbers within " 
              << timeout << L" ms." << endl;
        return true;
    }
    catch (const task_canceled& e)
    {
        wcout << L"The task timed out." << endl;
        return false;
    }
}

int wmain()
{
    // Compute the count of prime numbers in the range [0, 100000]  
    // until the operation fails. 
    // Each time the test succeeds, the time limit is halved. 

    unsigned int max = 100000;
    unsigned int timeout = 5000;

    bool success = true;
    do
    {
        success = count_primes(max, timeout);
        timeout /= 2;
    } while (success);
}
/* Sample output:
    Found 9592 prime numbers within 5000 ms.
    Found 9592 prime numbers within 2500 ms.
    Found 9592 prime numbers within 1250 ms.
    Found 9592 prime numbers within 625 ms.
    The task timed out.
*/

코드 컴파일

코드를 컴파일하려면 코드를 복사한 다음, Visual Studio 프로젝트 또는 task-delay.cpp 파일에 붙여넣고 Visual Studio 명령 프롬프트 창에서 다음 명령을 실행합니다.

cl.exe /EHsc task-delay.cpp

참고 항목

참조

작업 클래스(동시성 런타임)

cancellation_token_source 클래스

cancellation_token 클래스

task_completion_event 클래스

is_task_cancellation_requested 함수

cancel_current_task 함수

interruption_point 함수

timer 클래스

call 클래스

개념

작업 병렬 처리(동시성 런타임)

비동기 메시지 블록

PPL에서의 취소