Sdílet prostřednictvím


Zpracování výjimek v souběžném běhu

Runtime souběžnosti používá komunikaci různé druhy chyb zpracování výjimek C++.Tyto chyby obsahovat neplatné použití runtime, chyby runtime jako selhání získání zdroje a chybách pracovní funkce, které poskytují úkoly a skupiny úloh.Při úkolu nebo skupiny úloh vyvolá výjimku, runtime obsahuje výjimku a marshals ke kontextu čeká úkolu nebo skupiny úloh.Součástí lehký úkoly a agenti nespravuje runtime výjimky pro vás.V těchto případech musí implementovat vlastní mechanismus zpracování výjimek.Toto téma popisuje způsob zpracování modulu runtime výjimky odsunuté úkoly, skupiny úloh, lehké úkoly a asynchronní agenti a jak reagovat na výjimky v aplikacích.

Klíč bodů

  • Při úkolu nebo skupiny úloh vyvolá výjimku, runtime obsahuje výjimku a marshals ke kontextu čeká úkolu nebo skupiny úloh.

  • Pokud možno obklopují každé volání concurrency::task::get a concurrency::task::wait se try/catch bloku pro zpracování chyb, které můžete obnovit.Runtime aplikace ukončí, pokud úkol vyvolá výjimku a není výjimce podle úkolů, jeden z jeho continuations nebo hlavní aplikace.

  • Pokračování vždy podle úloh spustí; nezáleží, zda antecedent úloha dokončena úspěšně, došlo k výjimce nebo byla zrušena.Pokračování hodnota založena nespustí, pokud antecedent úkolu způsobí nebo zruší.

  • Protože podle úloh continuations vždy spustit, zvažte, zda přidat na konec řetězce v pokračování pokračování podle úloh.Toto může pomoci zajistit, aby váš kód dodržuje všechny výjimky.

  • Vyvolá modul runtime concurrency::task_canceled při volání concurrency::task::get a úkol je zrušena.

  • Modul runtime nespravuje výjimky pro lehké úkoly a agentů.

V tomto dokumentu

  • Úkoly a Continuations

  • Skupiny úkolů a paralelní algoritmy

  • Výjimek vyvolaných runtime

  • Více výjimek

  • Zrušení

  • Lehký úkoly

  • Asynchronní agenti

Úkoly a Continuations

Tato část popisuje způsob zpracování modulu runtime výjimky, které jsou vyvolané concurrency::task objekty a jejich continuations.Další informace o úkolu a pokračování model, viz Úkol rovnoběžnosti (souběžnosti Runtime).

Když vyvoláním výjimky v těle předáte funkci práce task objektu modulu runtime ukládá výjimku a marshals kontextu, který volá concurrency::task::get nebo concurrency::task::wait.Dokument Úkol rovnoběžnosti (souběžnosti Runtime) popisuje pokračování hodnota založena podle úloh versus continuations hodnota založena, ale chcete sumarizovat, přebírá parametr typu T a pokračování podle úloh přebírá parametr typu task<T>.Pokud má úkol, který vyvolá jeden nebo více založené na hodnotu continuations, nejsou tyto continuations naplánováno spuštění.Následující příklad demonstruje toto chování:

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

using namespace concurrency;
using namespace std;

int wmain()
{
    wcout << L"Running a task..." << endl;
    // Create a task that throws.
    auto t = create_task([]
    {
        throw exception();
    });

    // Create a continuation that prints its input value.
    auto continuation = t.then([]
    {
        // We do not expect this task to run because
        // the antecedent task threw.
        wcout << L"In continuation task..." << endl;
    });

    // Wait for the continuation to finish and handle any 
    // error that occurs.
    try
    {
        wcout << L"Waiting for tasks to finish..." << endl;
        continuation.wait();

        // Alternatively, call get() to produce the same result.
        //continuation.get();
    }
    catch (const exception& e)
    {
        wcout << L"Caught exception." << endl;
    }
}
/* Output:
    Running a task...
    Waiting for tasks to finish...
    Caught exception.
*/

Pokračování podle úloh umožňuje zpracovat všechny výjimky, jež je vyvolána antecedent úkolu.Pokračování vždy podle úloh spustí; nezáleží, zda úloha dokončena úspěšně, došlo k výjimce nebo byla zrušena.Při úkolu vyvolá výjimku, jsou jeho continuations založené na úkolech naplánováno spuštění.Následující příklad ukazuje úkol, který vždy vyvolá.Úkol má dvě continuations; jedna je hodnota založena a druhé podle úloh.Výjimka podle úloh spustí a proto může zachytit výjimku, která vyvolá antecedent úkolu.Při příklad čeká i continuations dokončit, je výjimka znovu protože úkolu je vždy vyvolána výjimka při task::get nebo task::wait se nazývá.

// eh-continuations.cpp
// compile with: /EHsc
#include <ppltasks.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{    
    wcout << L"Running a task..." << endl;
    // Create a task that throws.
    auto t = create_task([]() -> int
    {
        throw exception();
        return 42;
    });

    //
    // Attach two continuations to the task. The first continuation is  
    // value-based; the second is task-based.

    // Value-based continuation.
    auto c1 = t.then([](int n)
    {
        // We don't expect to get here because the antecedent 
        // task always throws.
        wcout << L"Received " << n << L'.' << endl;
    });

    // Task-based continuation.
    auto c2 = t.then([](task<int> previousTask)
    {
        // We do expect to get here because task-based continuations
        // are scheduled even when the antecedent task throws.
        try
        {
            wcout << L"Received " << previousTask.get() << L'.' << endl;
        }
        catch (const exception& e)
        {
            wcout << L"Caught exception from previous task." << endl;
        }
    });

    // Wait for the continuations to finish.
    try
    {
        wcout << L"Waiting for tasks to finish..." << endl;
        (c1 && c2).wait();
    }
    catch (const exception& e)
    {
        wcout << L"Caught exception while waiting for all tasks to finish." << endl;
    }
}
/* Output:
    Running a task...
    Waiting for tasks to finish...
    Caught exception from previous task.
    Caught exception while waiting for all tasks to finish.
*/

Doporučujeme použít podle úloh continuations zachytit výjimky, které je možné zpracovat.Protože podle úloh continuations vždy spustit, zvažte, zda přidat na konec řetězce v pokračování pokračování podle úloh.Toto může pomoci zajistit, aby váš kód dodržuje všechny výjimky.Následující příklad ukazuje základní pokračování založené na hodnotu řetězce.Vyvolá třetím úkolu v řetězci, a proto nejsou založené na hodnotu continuations, jeho spuštění.Závěrečné pokračování je však podle úloh a proto vždy spustí.Toto konečné pokračování zpracovává výjimku, která je vyvolána třetí úkol.

Doporučujeme nejvíce specifické výjimky, které lze o úlovku.Toto konečné pokračování podle úloh můžete vynechat, pokud nemáte specifické výjimky k odlovu.Všechny výjimky zůstanou neošetřené a může ukončit aplikace.

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

using namespace concurrency;
using namespace std;

int wmain()
{
    int n = 1;
    create_task([n]
    {
        wcout << L"In first task. n = ";
        wcout << n << endl;

        return n * 2;

    }).then([](int n)
    {
        wcout << L"In second task. n = ";
        wcout << n << endl;

        return n * 2;

    }).then([](int n)
    {
        wcout << L"In third task. n = ";
        wcout << n << endl;

        // This task throws.
        throw exception();
        // Not reached.
        return n * 2;

    }).then([](int n)
    {
        // This continuation is not run because the previous task throws.
        wcout << L"In fourth task. n = ";
        wcout << n << endl;

        return n * 2;

    }).then([](task<int> previousTask)
    {
        // This continuation is run because it is value-based.
        try
        {
            // The call to task::get rethrows the exception.
            wcout << L"In final task. result = ";
            wcout << previousTask.get() << endl;
        }
        catch (const exception&)
        {
            wcout << L"<exception>" << endl;
        }
    }).wait();
}
/* Output:
    In first task. n = 1
    In second task. n = 2
    In third task. n = 4
    In final task. result = <exception>
*/

Tip

Můžete použít concurrency::task_completion_event::set_exception metoda výjimku přidružit k události dokončení úkolu.Dokument Úkol rovnoběžnosti (souběžnosti Runtime) popisuje concurrency::task_completion_event třídy podrobněji.

CONCURRENCY::task_canceled je typ výjimky důležité runtime, který se vztahuje k task.Vyvolá modul runtime task_canceled při volání task::get a úkol je zrušena.(Naopak task::wait vrátí task_status::canceled a není vyvolána.) Můžete zachytit a zpracovat tuto výjimku z pokračování podle úloh nebo při volání task::get.Další informace o zrušení úkolu naleznete v Zrušení v PPL.

Poznámka k upozorněníUpozornění

Nikdy vyvoláním task_canceled v kódu.Volání concurrency::cancel_current_task místo.

Runtime aplikace ukončí, pokud úkol vyvolá výjimku a není výjimce podle úkolů, jeden z jeho continuations nebo hlavní aplikace.Pokud dojde k chybě aplikace, můžete nakonfigurovat Visual Studio přerušit při vyvolaných výjimek C++.Po můžete diagnostikovat umístění neošetřené výjimky, zpracování pomocí pokračování podle úloh.

V části Výjimky odsunuté Runtime v tomto dokumentu popisuje způsob práce s výjimkami runtime podrobněji.

Top

Skupiny úkolů a paralelní algoritmy

Tato část popisuje zpracování výjimek vyvolaných skupin úloh modulu runtime.V této části platí také pro paralelní algoritmy jako concurrency::parallel_for, protože tyto algoritmy sestavení skupiny úkolů.

Poznámka k upozorněníUpozornění

Ujistěte se, že rozumíte efekty, které výjimky jsou závislé úkoly.Doporučené postupy jak s úkoly nebo paralelní algoritmy zpracování výjimek naleznete Understand how Cancellation and Exception Handling Affect Object Destruction části Doporučené postupy v tématu paralelní knihovnu vzorků.

Další informace o skupinách úkolů naleznete v Úkol rovnoběžnosti (souběžnosti Runtime).Další informace o paralelní algoritmy Paralelní algoritmy.

Když vyvoláním výjimky v těle předáte funkci práce concurrency::task_group nebo concurrency::structured_task_group objektu modulu runtime ukládá výjimku a marshals kontextu, který volá concurrency::task_group::wait, concurrency::structured_task_group::wait, concurrency::task_group::run_and_wait, nebo concurrency::structured_task_group::run_and_wait.Modul runtime také zastaví všechny aktivní úkoly, které jsou ve skupině úloh (včetně podřízených skupin úkolů) a zahodí dosud nezahájené úkoly.

Následující příklad zobrazuje základní strukturu pracovní funkce, která vyvolá výjimku.V příkladu task_group objektu k tisku hodnot dvou point objektů současně.print_point Tiskne hodnoty funkce pro práci point objekt do konzoly.Pracovní funkce vyvolá výjimku, pokud je vstupní hodnota NULL.Modul runtime ukládá tato výjimka a marshals kontextu, který volá task_group::wait.

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

using namespace concurrency;
using namespace std;

// Defines a basic point with X and Y coordinates.
struct point
{
   int X;
   int Y;
};

// Prints the provided point object to the console.
void print_point(point* pt)
{
   // Throw an exception if the value is NULL.
   if (pt == NULL)
   {
      throw exception("point is NULL.");
   }

   // Otherwise, print the values of the point.
   wstringstream ss;
   ss << L"X = " << pt->X << L", Y = " << pt->Y << endl;
   wcout << ss.str();
}

int wmain()
{
   // Create a few point objects.
   point pt = {15, 30};
   point* pt1 = &pt;
   point* pt2 = NULL;

   // Use a task group to print the values of the points.
   task_group tasks;

   tasks.run([&] {
      print_point(pt1);
   });

   tasks.run([&] {
      print_point(pt2);
   });

   // Wait for the tasks to finish. If any task throws an exception,
   // the runtime marshals it to the call to wait.
   try
   {
      tasks.wait();
   }
   catch (const exception& e)
   {
      wcerr << L"Caught exception: " << e.what() << endl;
   }
}

Tento příklad vytvoří následující výstup.

X = 15, Y = 30
Caught exception: point is NULL.

Kompletní příklad, který používá zpracování výjimek v skupiny úloh, viz Jak: použití zpracování přestávku na paralelní smyčku z výjimek.

Top

Výjimek vyvolaných runtime

Výjimky mohou být výsledkem volání modulu runtime.Většina typů výjimku, s výjimkou concurrency::task_canceled a concurrency::operation_timed_out, chybné označení.Tyto chyby jsou obvykle neodstranitelné a proto by neměly být ulovených nebo zpracovány kódem aplikace.Doporučujeme pouze odlovu nebo potřebujete-li diagnostikovat programové chyby zpracování neodstranitelné chyby kód aplikace.Však Principy typy výjimek, které jsou definovány runtime můžete diagnostikovat chyby v programování.

Zpracování mechanismus výjimek je stejný výjimek vyvolaných runtime jako výjimky, které jsou vyvolána funkce práce.Například concurrency::receive vyvolá funkci operation_timed_out pokud neobdrží ani zprávy v určeném časovém intervalu.Pokud receive vyvolá výjimku ve funkci práce předat úkol skupiny, ukládá výjimku modulu runtime a marshals kontextu, který volá task_group::wait, structured_task_group::wait, task_group::run_and_wait, nebo structured_task_group::run_and_wait.

V následujícím příkladu concurrency::parallel_invoke algoritmus dva úkoly probíhají souběžně.První úkol čeká pět sekund a potom odešle zprávu vyrovnávací paměť pro zprávy.Druhý úkol používá receive funkce čekat tři sekundy zpráva ze stejné vyrovnávací paměť pro zprávy.receive Vyvolá funkci operation_timed_out pokud neobdrží zprávu v časovém období.

// eh-time-out.cpp
// compile with: /EHsc
#include <agents.h>
#include <ppl.h>
#include <iostream>

using namespace concurrency;
using namespace std;

int wmain()
{
   single_assignment<int> buffer;
   int result;

   try
   {
      // Run two tasks in parallel.
      parallel_invoke(
         // This task waits 5 seconds and then sends a message to 
         // the message buffer.
         [&] {
            wait(5000); 
            send(buffer, 42);
         },
         // This task waits 3 seconds to receive a message.
         // The receive function throws operation_timed_out if it does 
         // not receive a message in the specified time period.
         [&] {
            result = receive(buffer, 3000);
         }
      );

      // Print the result.
      wcout << L"The result is " << result << endl;
   }
   catch (operation_timed_out&)
   {
      wcout << L"The operation timed out." << endl;
   }
}

Tento příklad vytvoří následující výstup.

The operation timed out.

Neobvyklé ukončení aplikace, ujistěte, že váš kód zpracovává výjimky při zavolá modulu runtime.Při volání do externího kódu, který používá Runtime souběžnosti například knihovny výrobců také zpracovávat výjimky.

Top

Více výjimek

Pokud úkol nebo paralelní algoritmus obdrží více výjimek, runtime marshals pouze jeden z těchto výjimek v kontextu volajícího.Modul runtime nezaručuje výjimku, která jej marshals.

V následujícím příkladu parallel_for algoritmus k tisku čísel do konzoly.To vyvolá výjimku, pokud je vstupní hodnota menší než minimální hodnotu nebo větší než maximální hodnotu.V tomto příkladu můžete více pracovních funkcí vyvoláním výjimky.

// eh-multiple.cpp
// compile with: /EHsc
#include <ppl.h>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

int wmain()
{
   const int min = 0;
   const int max = 10;

   // Print values in a parallel_for loop. Use a try-catch block to 
   // handle any exceptions that occur in the loop.
   try
   {
      parallel_for(-5, 20, [min,max](int i)
      {
         // Throw an exeception if the input value is less than the 
         // minimum or greater than the maximum.

         // Otherwise, print the value to the console.

         if (i < min)
         {
            stringstream ss;
            ss << i << ": the value is less than the minimum.";
            throw exception(ss.str().c_str());
         }
         else if (i > max)
         {
            stringstream ss;
            ss << i << ": the value is greater than than the maximum.";
            throw exception(ss.str().c_str());
         }
         else
         {
            wstringstream ss;
            ss << i << endl;
            wcout << ss.str();
         }
      });
   }
   catch (exception& e)
   {
      // Print the error to the console.
      wcerr << L"Caught exception: " << e.what() << endl;
   }  
}

Následující příklad zobrazuje ukázkový výstup pro tento příklad.

8
2
9
3
10
4
5
6
7
Caught exception: -5: the value is less than the minimum.

Top

Zrušení

Všechny výjimky udávající chybu.Algoritmus vyhledávání může například pomocí zpracování výjimek najde výsledek ukončení jeho přidruženého úkolu.Další informace o zrušení mechanismy v kódu Zrušení v PPL.

Top

Lehký úkoly

Lehký úkol je úkol naplánovat přímo concurrency::Scheduler objektu.Lehký úkoly provádět menší nároky než běžné úkoly.Modul runtime nezachytí výjimek vyvolaných lehký úkolů.Místo toho výjimka zachycena popisovač neošetřené výjimky, které ukončí proces ve výchozím nastavení.Proto používejte vhodný mechanismus zpracování chyb v aplikaci.Další informace o úkolech lehký, viz Plánovač úloh (souběžnosti Runtime).

Top

Asynchronní agenti

Stejně jako lehký úkoly runtime správu výjimek vyvolaných asynchronní agenty.

Následující příklad ukazuje jeden způsob zpracování výjimek v třídě, který je odvozen od concurrency::agent.Tento příklad definuje points_agent třídy.points_agent::run Metoda čtení point objekty z vyrovnávací paměti pro zprávy a vytiskne je ke konzole.run Metoda vyvolá výjimku, pokud obdrží NULL ukazatel.

run Metoda ohraničuje všechny práce try-catch bloku.catch Bloku ukládá výjimku vyrovnávací paměť pro zprávy.Aplikace zkontroluje, zda agent zjistil chybu pomocí čtení z této vyrovnávací paměti po ukončení agenta.

// eh-agents.cpp
// compile with: /EHsc
#include <agents.h>
#include <iostream>

using namespace concurrency;
using namespace std;

// Defines a point with x and y coordinates.
struct point
{
   int X;
   int Y;
};

// Informs the agent to end processing.
point sentinel = {0,0};

// An agent that prints point objects to the console.
class point_agent : public agent
{
public:
   explicit point_agent(unbounded_buffer<point*>& points)
      : _points(points)
   { 
   }

   // Retrieves any exception that occurred in the agent.
   bool get_error(exception& e)
   {
      return try_receive(_error, e);
   }

protected:
   // Performs the work of the agent.
   void run()
   {
      // Perform processing in a try block.
      try
      {
         // Read from the buffer until we reach the sentinel value.
         while (true)
         {
            // Read a value from the message buffer.
            point* r = receive(_points);

            // In this example, it is an error to receive a 
            // NULL point pointer. In this case, throw an exception.
            if (r == NULL)
            {
               throw exception("point must not be NULL");
            }
            // Break from the loop if we receive the 
            // sentinel value.
            else if (r == &sentinel)
            {
               break;
            }
            // Otherwise, do something with the point.
            else
            {
               // Print the point to the console.
               wcout << L"X: " << r->X << L" Y: " << r->Y << endl;
            }
         }
      }
      // Store the error in the message buffer.
      catch (exception& e)
      {
         send(_error, e);
      }

      // Set the agent status to done.
      done();
   }

private:
   // A message buffer that receives point objects.
   unbounded_buffer<point*>& _points;

   // A message buffer that stores error information.
   single_assignment<exception> _error;
};

int wmain()
{  
   // Create a message buffer so that we can communicate with
   // the agent.
   unbounded_buffer<point*> buffer;

   // Create and start a point_agent object.
   point_agent a(buffer);
   a.start();

   // Send several points to the agent.
   point r1 = {10, 20};
   point r2 = {20, 30};
   point r3 = {30, 40};

   send(buffer, &r1);
   send(buffer, &r2);
   // To illustrate exception handling, send the NULL pointer to the agent.
   send(buffer, reinterpret_cast<point*>(NULL));
   send(buffer, &r3);
   send(buffer, &sentinel);

   // Wait for the agent to finish.
   agent::wait(&a);

   // Check whether the agent encountered an error.
   exception e;
   if (a.get_error(e))
   {
      cout << "error occurred in agent: " << e.what() << endl;
   }

   // Print out agent status.
   wcout << L"the status of the agent is: ";
   switch (a.status())
   {
   case agent_created:
      wcout << L"created";
      break;
   case agent_runnable:
      wcout << L"runnable";
      break;
   case agent_started:
      wcout << L"started";
      break;
   case agent_done:
      wcout << L"done";
      break;
   case agent_canceled:
      wcout << L"canceled";
      break;
   default:
      wcout << L"unknown";
      break;
   }
   wcout << endl;
}

Tento příklad vytvoří následující výstup.

X: 10 Y: 20
X: 20 Y: 30
error occurred in agent: point must not be NULL
the status of the agent is: done

Protože try-catch bloku existuje mimo while smyčky, agent ukončí zpracování při nalezení první chyby.Pokud try-catch blok byl uvnitř while smyčky, agent by pokračovat po dojde k chybě.

V tomto příkladu ukládá výjimky ve vyrovnávací paměti zprávy jiné komponenty můžete sledovat agenta chyb při jeho spuštění.V tomto příkladu concurrency::single_assignment objekt uložit Chyba.V případě, kdy agent zpracovává více výjimek single_assignment třídy ukládá pouze první zprávy předané k němu.Uložit pouze poslední výjimka, použít concurrency::overwrite_buffer třídy.Uložit všechny výjimky, použít concurrency::unbounded_buffer třídy.Další informace o těchto bloků zprávy, viz Asynchronní bloků zprávy.

Další informace o asynchronní agenti, viz Asynchronní agenti.

Top

SHRNUTÍ

Top

Viz také

Koncepty

Souběžnost Runtime

Úkol rovnoběžnosti (souběžnosti Runtime)

Paralelní algoritmy

Zrušení v PPL

Plánovač úloh (souběžnosti Runtime)

Asynchronní agenti