Compartilhar via


Cancelamento no PPL

Este documento explica a função de cancelamento na biblioteca (PPL) de padrões de paralela, como cancelar o trabalho paralelo, e como determinar quando o trabalho for cancelado paralelos.

Dica

O tempo de execução usa a manipulação de exceção para implementar o cancelamento.Não capturar nem manipula essas exceções em seu código.Além disso, recomendamos que você escreve o código exceções gerais seguro nos corpos da função para suas tarefas.Por exemplo, você pode usar o padrão Aquisição de recurso é inicialização (RAII) para garantir que os recursos do sejam tratados corretamente quando uma exceção é lançada no corpo de uma tarefa.Para obter um exemplo completo que use o padrão de RAII para limpar um recurso em uma tarefa cancelável, consulte Instruções passo a passo: removendo trabalho de um thread de interface de usuário.

Pontos-chave

  • O cancelamento é cooperativo e envolve a coordenação entre o código que solicitou o cancelamento e a tarefa que responde ao cancelamento.

  • Quando possível, tokens de cancelamento de uso para cancelar o trabalho. A classe de concurrency::cancellation_token define um token de cancelamento.

  • Quando você usar tokens de cancelamento, use o método de concurrency::cancellation_token_source::cancel para iniciar o cancelamento e funções de concurrency::is_task_cancellation_requested e de concurrency::cancel_current_task para responder a ser cancelado.

  • O cancelamento não acontecerá imediatamente. Embora o novo trabalho não seja iniciada se uma tarefa ou um grupo de trabalho são canceladas, o trabalho ativo deve verificar para e responder ao cancelamento.

  • Um acompanhamento baseada em valor herda o token de cancelamento da tarefa antecedente. Um acompanhamento de tarefas com base nunca herda o token da tarefa antecedente.

  • Use o método de concurrency::cancellation_token::none quando você chama um construtor ou função que faça um objeto de cancellation_token mas não quer que a operação a ser cancelável. Além disso, se você não passar um token de cancelamento ao construtor de concurrency::task ou à função de concurrency::create_task , essa tarefa não é cancelável.

Neste Documento

  • Árvores de Trabalho Paralelas

  • Cancelando Tarefas Paralelas

    • Usando um Token de Cancelamento para Cancelar o Trabalho Paralelo

    • Usando o Método cancel para Cancelar o Trabalho Paralelo

    • Usando Exceções para Cancelar Trabalho Paralelo

  • Cancelando Algoritmos Paralelos

  • Quando Não Usar Cancelamento

Árvores de Trabalho Paralelas

O PPL usa tarefas e grupos de trabalho gerenciar tarefas refinados e computações. É possível aninhar grupos de trabalho para formar árvores de trabalho paralelo. A ilustração a seguir mostra uma árvore paralela de trabalho. Nesta ilustração, tg1 e tg2 representam grupos de trabalho; t1, t2, t3, t4, e t5 representam o trabalho que os grupos de trabalho é executada.

Uma árvore de trabalho paralelo

O exemplo a seguir mostra o código que é exigido para criar a árvore na ilustração a seguir. Neste exemplo, tg1 e tg2 são objetos de concurrency::structured_task_group ; t1, t2, t3, t4, e t5 são objetos de concurrency::task_handle .

// task-tree.cpp 
// compile with: /c /EHsc
#include <ppl.h>
#include <sstream>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

void create_task_tree()
{   
   // Create a task group that serves as the root of the tree.
   structured_task_group tg1;

   // Create a task that contains a nested task group.
   auto t1 = make_task([&] {
      structured_task_group tg2;

      // Create a child task.
      auto t4 = make_task([&] {
         // TODO: Perform work here.
      });

      // Create a child task.
      auto t5 = make_task([&] {
         // TODO: Perform work here.
      });

      // Run the child tasks and wait for them to finish.
      tg2.run(t4);
      tg2.run(t5);
      tg2.wait();
   });

   // Create a child task.
   auto t2 = make_task([&] {
      // TODO: Perform work here.
   });

   // Create a child task.
   auto t3 = make_task([&] {
      // TODO: Perform work here.
   });

   // Run the child tasks and wait for them to finish.
   tg1.run(t1);
   tg1.run(t2);
   tg1.run(t3);
   tg1.wait();   
}

Você também pode usar a classe de concurrency::task_group para criar uma árvore semelhante de trabalho. A classe de concurrency::task também oferece suporte à noção de uma árvore de trabalho. No entanto, uma árvore de task é uma árvore de dependência. Em uma árvore de task futuros, o trabalho terminar depois do trabalho atual. Em uma árvore de grupo de trabalho, o trabalho interno for concluída antes de trabalho externo. Para obter mais informações sobre as diferenças entre tarefas e grupos de trabalho, consulte Paralelismo de tarefa (tempo de execução de simultaneidade).

[Superior]

Cancelando Tarefas Paralelas

Há várias maneiras de cancelar o trabalho paralelo. O modo preferido é usar um token de cancelamento. Os grupos de trabalho também dão suporte ao método de concurrency::task_group::cancel e o método de concurrency::structured_task_group::cancel . A forma final é gerar uma exceção no corpo de uma função de trabalho da tarefa. Independentemente do método escolhido, de que o cancelamento não acontecerá imediatamente. Embora o novo trabalho não seja iniciada se uma tarefa ou um grupo de trabalho são canceladas, o trabalho ativo deve verificar para e responder ao cancelamento.

Para obter mais exemplos que cancelam tarefas paralelas, consulte Instruções passo a passo: conexão usando tarefas e solicitações HTTP XML, Como usar cancelamento para parar um loop paralelo, e Como usar tratamento de exceções para parar um loop paralelo.

Usando um Token de Cancelamento para Cancelar o Trabalho Paralelo

task, task_group, e as classes de structured_task_group dão suporte ao cancelamento com o uso de tokens de cancelamento. O PPL define as classes de concurrency::cancellation_token_source e de concurrency::cancellation_token para essa finalidade. Quando você usa um token de cancelar para cancelar o trabalho, o tempo de execução não inicia um novo trabalho que assina o token. Trabalhar que esteja ativo pode monitorar seu token de cancelamento e o parar quando possível.

Para iniciar o cancelamento, chame o método de concurrency::cancellation_token_source::cancel . Responder cancelamento destas maneiras:

O exemplo a seguir mostra o primeiro padrão básico para o cancelamento da tarefa. O corpo da tarefa ocasionalmente verifica a existência do cancelamento em um loop.

// task-basic-cancellation.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

bool do_work()
{
    // Simulate work.
    wcout << L"Performing work..." << endl;
    wait(250);
    return true;
}

int wmain()
{
    cancellation_token_source cts;
    auto token = cts.get_token();

    wcout << L"Creating task..." << endl;

    // Create a task that performs work until it is canceled.
    auto t = create_task([]
    {
        bool moreToDo = true;
        while (moreToDo)
        {
            // Check for cancellation. 
            if (is_task_cancellation_requested())
            {
                // TODO: Perform any necessary cleanup here... 

                // Cancel the current task.
                cancel_current_task();
            }
            else 
            {
                // Perform work.
                moreToDo = do_work();
            }
        }
    }, token);

    // Wait for one second and then cancel the task.
    wait(1000);

    wcout << L"Canceling task..." << endl;
    cts.cancel();

    // Wait for the task to cancel.
    wcout << L"Waiting for task to complete..." << endl;
    t.wait();

    wcout << L"Done." << endl;
}

/* Sample output:
    Creating task...
    Performing work...
    Performing work...
    Performing work...
    Performing work...
    Canceling task...
    Waiting for task to complete...
    Done.
*/

Lançará da função de cancel_current_task ; consequentemente, você não precisa ser retornadas do loop atual ou de funcionar explicitamente.

Dica

Como alternativa, você pode chamar a função de concurrency::interruption_point em vez de is_task_cancellation_requested e de cancel_current_task.

É importante quando você chamar cancel_current_task responde ao cancelamento porque faz a transição da tarefa para o estado cancelado. Se você retorna no início em vez de chamar cancel_current_task, a operação faz a transição para o estado concluído e todas as continuações - baseiam são executadas.

Aviso

Nunca gerencie task_canceled de seu código.Chame o cancel_current_task em vez disso.

Quando a conclusão da tarefa no estado cancelado, o método de concurrency::task::get jogarem concurrency::task_canceled. (Por outro lado, concurrency::task::wait retorna task_status::canceled e não o lança.) O exemplo a seguir ilustra esse comportamento para uma tarefa acompanhamento baseada. Um acompanhamento de tarefas com base sempre será chamada, mesmo quando a tarefa antecedente é cancelada.

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

using namespace concurrency;
using namespace std;

int wmain()
{
    auto t1 = create_task([]() -> int
    {
        // Cancel the task.
        cancel_current_task();
    });

    // Create a continuation that retrieves the value from the previous.
    auto t2 = t1.then([](task<int> t)
    {
        try
        {
            int n = t.get();
            wcout << L"The previous task returned " << n << L'.' << endl;
        }
        catch (const task_canceled& e)
        {
            wcout << L"The previous task was canceled." << endl;
        }
    });

    // Wait for all tasks to complete.
    t2.wait();
}
/* Output:
    The previous task was canceled.
*/

Como as continuações valores com base herdam o token da tarefa antecedente a menos que sejam criadas com um token explícito, as continuações imediatamente entram no estado cancelado mesmo quando a tarefa antecedente ainda estiver em execução. Em virtude disso, qualquer exceção que é gerada pela tarefa antecedente depois que o cancelamento não é propagado na tarefas de acompanhamento. O cancelamento sempre substitui o estado da tarefa antecedente. O exemplo a seguir é semelhante ao anterior, mas ilustra-se o comportamento de uma acompanhamento baseada em valor.

auto t1 = create_task([]() -> int
{
    // Cancel the task.
    cancel_current_task();
});

// Create a continuation that retrieves the value from the previous.
auto t2 = t1.then([](int n)
{
    wcout << L"The previous task returned " << n << L'.' << endl;
});

try
{
    // Wait for all tasks to complete.
    t2.get();
}
catch (const task_canceled& e)
{
    wcout << L"The task was canceled." << endl;
}
/* Output:
    The task was canceled.
*/

Aviso

Se você não passar um token de cancelamento ao construtor de task ou à função de concurrency::create_task , essa tarefa não é cancelável.Além disso, você deve transmitir o mesmo token de cancelamento ao construtor de todas as tarefas aninhadas (ou seja, tarefas que são criadas no corpo de uma outra tarefa) cancelar simultaneamente todas as tarefas.

Talvez você queira executar códigos arbitrários quando um token de cancelamento for cancelado. Por exemplo, se o usuário escolher um botão de Cancelar na interface do usuário para cancelar a operação, você pode desabilitar esse botão até que o usuário iniciar outra. O exemplo a seguir mostra como usar o método de concurrency::cancellation_token::register_callback para registrar uma função de retorno de chamada que é executado quando um token de cancelamento for cancelado.

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

using namespace concurrency;
using namespace std;

int wmain()
{
    cancellation_token_source cts;
    auto token = cts.get_token();

    // An event that is set in the cancellation callback. 
    event e;

    cancellation_token_registration cookie;
    cookie = token.register_callback([&e, token, &cookie]()
    {
        wcout << L"In cancellation callback..." << endl;
        e.set();

        // Although not required, demonstrate how to unregister  
        // the callback.
        token.deregister_callback(cookie);
    });

    wcout << L"Creating task..." << endl;

    // Create a task that waits to be canceled.
    auto t = create_task([&e]
    {
        e.wait();
    }, token);

    // Cancel the task.
    wcout << L"Canceling task..." << endl;
    cts.cancel();

    // Wait for the task to cancel.
    t.wait();

    wcout << L"Done." << endl;
}
/* Sample output:
    Creating task...
    Canceling task...
    In cancellation callback...
    Done.
*/

O documento Paralelismo de tarefa (tempo de execução de simultaneidade) explica a diferença entre continuações valores base e baseadas. Se você não fornecer um objeto de cancellation_token a uma tarefa de acompanhamento, a continuação herda o token de cancelamento da tarefa antecedente das seguintes maneiras:

  • Um acompanhamento baseada em valor sempre herda o token de cancelamento da tarefa antecedente.

  • Um acompanhamento de tarefas com base nunca herda o token de cancelamento da tarefa antecedente. A única maneira de fazer uma acompanhamento de tarefas com base é cancelável explicitamente transmitir um token de cancelamento.

Esses comportamentos não são afetados por uma tarefa criticada (ou seja, uma que gerará uma exceção). Nesse caso, um acompanhamento baseada em valor é cancelada; um acompanhamento de tarefas com base não é cancelada.

Aviso

Uma tarefa que é criada em outra tarefa (ou é uma tarefa aninhada) não herda o token de cancelamento da tarefa pai.Apenas uma acompanhamento baseada em valor herda o token de cancelamento da tarefa antecedente.

Dica

Use o método de concurrency::cancellation_token::none quando você chama um construtor ou função que faça um objeto de cancellation_token e você não quiser que a operação para ser cancelável.

Você também pode fornecer um token de cancelamento ao construtor de um objeto de task_group ou de structured_task_group . Um aspecto importante dos grupos de trabalho filhos herdam este token de cancelamento. Para obter um exemplo que demonstra este conceito usando a função de concurrency::run_with_cancellation_token para executar para chamar parallel_for, consulte Cancelando algoritmos paralelos mais adiante neste documento.

[Superior]

Tokens de Cancelamento e Composição da Tarefa

As funções de concurrency::when_all e de concurrency::when_any podem ajudá-lo a criar várias tarefas implementar padrões comuns. Esta seção descreve como essas funções trabalham com os tokens de cancelamento.

Quando você fornecer um token de cancelamento a um ou a função de when_all e de when_any , cancela dessa função apenas enquanto o token de cancelamento é cancelado ou quando uma das extremidades de tarefas do participante em um estado cancelado ou gerar uma exceção.

A função de when_all herda o token de cancelamento de cada tarefa que compõem a operação total quando você não lhe fornece um token de cancelamento. A tarefa que é retornado de when_all é cancelada quando algum dos tokens são cancelados e pelo menos um participante das tarefas que ainda não foram iniciados ou estão em execução. Um comportamento semelhante ocorre quando uma das tarefas gerará uma exceção – a tarefa retornada de when_all será cancelada imediatamente com essa exceção.

O tempo de execução escolhe o token de cancelamento da tarefa que é retornado da função de when_any quando essa tarefa é concluída. Se nenhuma das tarefas de participantes terminem em um estado concluído e uma ou mais das tarefas lança uma exceção, uma das tarefas que jogaram é escolhido para concluir when_any e seu token é escolhido como o token para a tarefa final. Se mais de uma tarefa é concluída no estado concluído, a tarefa que é retornada de conclusão da tarefa de when_any em um estado concluído. As tentativas de tempo de execução para escolher uma tarefa concluída cujo token não seja cancelado na hora da conclusão de forma que a tarefa retornada de when_any não são canceladas imediatamente mesmo que executar outras tarefas possam terminar em um momento posterior.

[Superior]

Usando o Método cancel para Cancelar o Trabalho Paralelo

Os métodos de concurrency::task_group::cancel e de concurrency::structured_task_group::cancel definem um grupo de trabalho para o estado cancelado. Depois de chamar cancel, o grupo de trabalho não inicia as tarefas futuras. Os métodos de cancel podem ser chamados por várias tarefas filhos. Uma tarefa cancelada faz com que os métodos de concurrency::task_group::wait e de concurrency::structured_task_group::wait retornem concurrency::canceled.

Se um grupo de trabalho for cancelado, chamadas de cada tarefa filho em tempo de execução podem disparar um ponto de interrupção, que faz com que o tempo de execução e capture gerencie um tipo de exceção interna para cancelar tarefas ativas. O tempo de execução de simultaneidade não define pontos específicos de interrupção; podem ocorrer em qualquer chamada para o tempo de execução. O tempo de execução deve manipular as exceções que gerencie para executar o cancelamento. Consequentemente, não tratar exceções desconhecidas no corpo de uma tarefa.

Se uma tarefa filho executa uma operação demorada e não a seguir chama em tempo de execução, periodicamente deve verificar o cancelamento e sair de maneira oportuna. O exemplo a seguir mostra uma maneira de determinar quando o trabalho for cancelado. A tarefa t4 cancela o grupo de trabalho pai quando encontra um erro. A tarefa t5 ocasionalmente chama o método de structured_task_group::is_canceling para verificar o cancelamento. Se o grupo de trabalho pai é cancelado, a tarefa t5 imprime uma mensagem e o será encerrado.

structured_task_group tg2;

// Create a child task.
auto t4 = make_task([&] {
   // Perform work in a loop. 
   for (int i = 0; i < 1000; ++i)
   {
      // Call a function to perform work. 
      // If the work function fails, cancel the parent task 
      // and break from the loop. 
      bool succeeded = work(i);
      if (!succeeded)
      {
         tg2.cancel();
         break;
      }
   }
});

// Create a child task.
auto t5 = make_task([&] {
   // Perform work in a loop. 
   for (int i = 0; i < 1000; ++i)
   {
      // To reduce overhead, occasionally check for  
      // cancelation. 
      if ((i%100) == 0)
      {
         if (tg2.is_canceling())
         {
            wcout << L"The task was canceled." << endl;
            break;
         }
      }

      // TODO: Perform work here.
   }
});

// Run the child tasks and wait for them to finish.
tg2.run(t4);
tg2.run(t5);
tg2.wait();

Este exemplo verifica o cancelamento em cada iteração do loop 100º da tarefa. A frequência com que você verifica o cancelamento dependem da quantidade de trabalho que a tarefa executa e como rapidamente é necessário para as tarefas respondam a ser cancelado.

Se você não tiver acesso ao objeto pai do grupo de trabalho, chame a função de concurrency::is_current_task_group_canceling para determinar se o grupo de trabalho pai será cancelado.

O método de cancel afeta apenas tarefas filhos. Por exemplo, se você cancelar o grupo de trabalho tg1 na ilustração de árvore paralela de trabalho, todas as tarefas na árvore (t1, t2, t3, t4, e t5) são afetadas. Se você cancelar o grupo de trabalho aninhado, tg2, somente as tarefas t4 e t5 são afetadas.

Quando você chama o método de cancel , todos os grupos de trabalho filho são cancelados também. Porém, o cancelamento não afeta os pais do grupo de trabalho em uma árvore paralela de trabalho. Os exemplos a seguir mostram este criando na ilustração paralela da árvore de trabalho.

O primeiro dos exemplos a seguir cria uma função de trabalho para a tarefa t4, que é um filho do grupo de trabalho tg2. As chamadas de função do trabalho a função work em um loop. Se qualquer chamada para work falhar, a tarefa cancela seu grupo de trabalho pai. Isso faz com que o grupo de trabalho tg2 entre o estado cancelado, mas não cancela o grupo de trabalho tg1.

auto t4 = make_task([&] {
   // Perform work in a loop. 
   for (int i = 0; i < 1000; ++i)
   {
      // Call a function to perform work. 
      // If the work function fails, cancel the parent task 
      // and break from the loop. 
      bool succeeded = work(i);
      if (!succeeded)
      {
         tg2.cancel();
         break;
      }
   }         
});

Este segundo exemplo se assemelha ao primeiro, exceto pelo fato do grupo de trabalho tg1de cancelamentos da tarefa. Isso afeta todas as tarefas na árvore (t1, t2, t3, t4, e t5).

auto t4 = make_task([&] {
   // Perform work in a loop. 
   for (int i = 0; i < 1000; ++i)
   {
      // Call a function to perform work. 
      // If the work function fails, cancel all tasks in the tree. 
      bool succeeded = work(i);
      if (!succeeded)
      {
         tg1.cancel();
         break;
      }
   }   
});

A classe de structured_task_group não é seguro para threads. Em virtude disso, uma tarefa filho que chama um método do objeto de structured_task_group do pai gerencia o comportamento não especificado. As exceções a essa regra são os métodos de structured_task_group::cancel e de concurrency::structured_task_group::is_canceling . Uma tarefa filho pode chamar esses métodos para cancelar o grupo de trabalho e a verificação pai para o cancelamento.

Aviso

Embora você possa usar um token de cancelar para cancelar o trabalho executado por um grupo de trabalho que executa como um filho de um objeto de task , você não pode usar os métodos de task_group::cancel ou de structured_task_group::cancel para cancelar os objetos de task executados em um grupo de trabalho.

[Superior]

Usando Exceções para Cancelar Trabalho Paralelo

O uso de tokens de cancelamento e o método de cancel são mais eficientes do que a manipulação de exceção em cancelar uma árvore paralela de trabalho. Os tokens de cancelamento e o método de cancel cancelar uma tarefa e todas as tarefas do filho em uma forma de reversão. Por outro lado, o trabalho de manipulação de exceção em uma forma de baixo para cima e devem cancelar cada grupo de trabalho filho independente como as propagações de exceção para cima. O Tratamento de exceções no tempo de execução de simultaneidade tópico explica como o tempo de execução de simultaneidade usa exceções para comunicar erros. Porém, nem todas as exceções indica um erro. Por exemplo, um algoritmo de pesquisa pode cancelar a tarefa associada quando encontrar o resultado. No entanto, como mencionado anteriormente, a manipulação de exceção é menos eficiente do que usar o método de cancel para cancelar o trabalho paralelo.

Aviso

Recomendamos que você use exceções para cancelar o trabalho paralelo apenas quando necessário.Os tokens de cancelamento e os métodos de cancel de grupo de trabalho é um erro mais eficiente e menos mais inclinado.

Quando você gerencie uma exceção no corpo de uma função de trabalho que você transmita a um grupo de trabalho, o tempo de execução que armazena exceção e realiza marshaling a exceção ao contexto aguardando o grupo de trabalho para concluir. Como com o método de cancel , o tempo de execução descarta todas as tarefas que ainda não sejam iniciados, e não aceitará novas tarefas.

O terceiro exemplo se assemelha ao segundo, exceto pelo fato de t4 da tarefa gera uma exceção para cancelar o grupo de trabalho tg2. Este exemplo usa um bloco de try- decatch para verificar cancelamentos quando o grupo de trabalho tg2 espera filhos concluir suas tarefas. Como o primeiro exemplo, isso faz com que o grupo de trabalho tg2 entre o estado cancelado, mas não cancela o grupo de trabalho tg1.

structured_task_group tg2;

// Create a child task.      
auto t4 = make_task([&] {
   // Perform work in a loop. 
   for (int i = 0; i < 1000; ++i)
   {
      // Call a function to perform work. 
      // If the work function fails, throw an exception to  
      // cancel the parent task. 
      bool succeeded = work(i);
      if (!succeeded)
      {
         throw exception("The task failed");
      }
   }         
});

// Create a child task.
auto t5 = make_task([&] {
   // TODO: Perform work here.
});

// Run the child tasks.
tg2.run(t4);
tg2.run(t5);

// Wait for the tasks to finish. The runtime marshals any exception 
// that occurs to the call to wait. 
try
{
   tg2.wait();
}
catch (const exception& e)
{
   wcout << e.what() << endl;
}

Essa quarta manipulação de exceção de exemplo usa para cancelar a árvore de trabalho inteira. O exemplo captura a exceção quando o grupo de trabalho tg1 espera filhos concluir suas tarefas em vez de quando o grupo de trabalho tg2 espera suas tarefas filhos. Como o segundo exemplo, isso faz com que os dois grupos de tarefas na árvore, em tg1 e em tg2, entra no estado cancelado.

// Run the child tasks.
tg1.run(t1);
tg1.run(t2);
tg1.run(t3);   

// Wait for the tasks to finish. The runtime marshals any exception 
// that occurs to the call to wait. 
try
{
   tg1.wait();
}
catch (const exception& e)
{
   wcout << e.what() << endl;
}

Como os métodos de task_group::wait e de structured_task_group::wait lançam quando uma tarefa filho gerou uma exceção, você não receberá um valor de retorno deless.

[Superior]

Cancelando Algoritmos Paralelos

Algoritmos paralelos em PPL, por exemplo, parallel_for, criação em grupos de trabalho. Em virtude disso, você pode usar muitas das mesmas técnicas cancelar um algoritmo paralelo.

Os exemplos a seguir ilustram várias maneiras de cancelar um algoritmo paralelo.

O exemplo a seguir usa a função de run_with_cancellation_token para chamar o algoritmo de parallel_for . A função de run_with_cancellation_token usa um token de cancelamento como um argumento e chama a função de trabalho fornecida de forma síncrona. Como os algoritmos paralelos são criados em cima de tarefas, herdam o token de cancelamento da tarefa pai. Consequentemente, parallel_for pode responder a ser cancelado.

// cancel-parallel-for.cpp 
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

int wmain()
{
    // Call parallel_for in the context of a cancellation token.
    cancellation_token_source cts;
    run_with_cancellation_token([&cts]() 
    {
        // Print values to the console in parallel.
        parallel_for(0, 20, [&cts](int n)
        {
            // For demonstration, cancel the overall operation  
            // when n equals 11. 
            if (n == 11)
            {
                cts.cancel();
            }
            // Otherwise, print the value. 
            else
            {
                wstringstream ss;
                ss << n << endl;
                wcout << ss.str();
            }
        });
    }, cts.get_token());
}
/* Sample output:
    15
    16
    17
    10
    0
    18
    5
*/

O exemplo a seguir usa o método de concurrency::structured_task_group::run_and_wait para chamar o algoritmo de parallel_for . O método de structured_task_group::run_and_wait espera fornecida concluir a tarefa. O objeto de structured_task_group habilita a função de trabalho para cancelar a tarefa.

// To enable cancelation, call parallel_for in a task group.
structured_task_group tg;

task_group_status status = tg.run_and_wait([&] {
   parallel_for(0, 100, [&](int i) {
      // Cancel the task when i is 50. 
      if (i == 50)
      {
         tg.cancel();
      }
      else
      {
         // TODO: Perform work here.
      }
   });
});

// Print the task group status.
wcout << L"The task group status is: ";
switch (status)
{
case not_complete:
   wcout << L"not complete." << endl;
   break;
case completed:
   wcout << L"completed." << endl;
   break;
case canceled:
   wcout << L"canceled." << endl;
   break;
default:
   wcout << L"unknown." << endl;
   break;
}

O exemplo produz a seguinte saída.

  

O exemplo a seguir usa a manipulação de exceção para cancelar um loop de parallel_for . Os realiza marshaling de tempo de execução a exceção ao contexto de chamada.

try
{
   parallel_for(0, 100, [&](int i) {
      // Throw an exception to cancel the task when i is 50. 
      if (i == 50)
      {
         throw i;
      }
      else
      {
         // TODO: Perform work here.
      }
   });
}
catch (int n)
{
   wcout << L"Caught " << n << endl;
}

O exemplo produz a seguinte saída.

  

O exemplo a seguir usa um sinalizador booliano para coordenar o cancelamento em um loop de parallel_for . Cada tarefa é executada porque este exemplo não usa a manipulação do método ou de exceção de cancel para cancelar o conjunto de geral de tarefas. Consequentemente, essa técnica pode ter mais sobrecarga computacional de que um mecanismo de cancelamento.

// Create a Boolean flag to coordinate cancelation. 
bool canceled = false;

parallel_for(0, 100, [&](int i) {
   // For illustration, set the flag to cancel the task when i is 50. 
   if (i == 50)
   {
      canceled = true;
   }

   // Perform work if the task is not canceled. 
   if (!canceled)
   {
      // TODO: Perform work here.
   }
});

Cada método de cancelamento tem vantagens em relação às outras. Escolha o método que se ajusta às suas necessidades específicas.

[Superior]

Quando Não Usar Cancelamento

O uso de cancelamento é apropriado quando cada membro de um grupo de tarefas relacionadas pode deixar de maneira oportuna. No entanto, há alguns cenários onde o cancelamento não pode ser apropriado para seu aplicativo. Por exemplo, porque o cancelamento da tarefa é cooperativo, o conjunto de geral de tarefas não cancelará se qualquer tarefa individual é bloqueada. Por exemplo, se uma tarefa ainda não foi iniciado, mas ele desbloqueia outra tarefa ativa, ele não será iniciado se o grupo de trabalho for cancelado. Isso pode fazer com que o deadlock ocorre em seu aplicativo. Um segundo exemplo de onde o uso de cancelamento não pode ser apropriado é quando uma tarefa é cancelada, mas sua tarefa filho executa uma operação importante, como liberar um recurso. Como o conjunto de geral de tarefas é cancelado quando a tarefa pai for cancelada, essa operação não será executado. Para obter um exemplo que mostra esse ponto, consulte a seção de Entenda como o efeito de manipulação de cancelamento e destruição objetos de exceção nas práticas recomendadas no tópico de biblioteca de padrões de paralela.

[Superior]

Tópicos relacionados

Nome

Descrição

Como usar cancelamento para parar um loop paralelo

Mostra como usar o cancelar para implementar um algoritmo de pesquisa paralela.

Como usar tratamento de exceções para parar um loop paralelo

Mostra como usar a classe de task_group para escrever um algoritmo de pesquisa para uma estrutura de árvore básica.

Tratamento de exceções no tempo de execução de simultaneidade

Descreve como o tempo de execução trata as exceções emitidas por grupos de trabalho, por tarefas de peso leve, e por agentes assíncronas, e como responder às exceções em seus aplicativos.

Paralelismo de tarefa (tempo de execução de simultaneidade)

Descreve como as tarefas relacionadas aos grupos de trabalho e como você pode usar tarefas não estruturados e estruturados em seus aplicativos.

Algoritmos paralelos

Descreve os algoritmos paralelos, que são executados simultaneamente o trabalho em coleções de dados

Biblioteca de padrões paralelos (PPL)

Fornece uma visão geral da biblioteca de padrões de paralela.

Referência

Classe task (Tempo de Execução de Simultaneidade)

Classe cancellation_token_source

Classe cancellation_token

Classe task_group

Classe structured_task_group

Função parallel_for