Delen via


Afhandeling van uitzonderingen in de Concurrency Runtime

De Gelijktijdigheidsruntime maakt gebruik van de verwerking van C++-uitzonderingen om veel soorten fouten te communiceren. Deze fouten omvatten een ongeldig gebruik van de runtime, runtimefouten zoals het niet verkrijgen van een resource en fouten die optreden in werkfuncties die u aan taken en taakgroepen levert. Wanneer een taak of taakgroep een uitzondering genereert, bevat de runtime deze uitzondering en wordt deze in de context geplaatst die wacht totdat de taak of taakgroep is voltooid. Voor onderdelen zoals lichtgewicht taken en agents beheert de runtime geen uitzonderingen voor u. In deze gevallen moet u uw eigen mechanisme voor het afhandelen van uitzonderingen implementeren. In dit onderwerp wordt beschreven hoe de runtime uitzonderingen verwerkt die worden gegenereerd door taken, taakgroepen, lichtgewicht taken en asynchrone agents, en hoe u kunt reageren op uitzonderingen in uw toepassingen.

Belangrijkste punten

  • Wanneer een taak of taakgroep een uitzondering genereert, bevat de runtime deze uitzondering en wordt deze in de context geplaatst die wacht totdat de taak of taakgroep is voltooid.

  • Plaats, indien mogelijk, elke aanroep naar gelijktijdigheid::task::get en gelijktijdigheid::task::wait binnen een try/catch-blok om om te gaan met fouten waarvan u kunt herstellen. Het runtime-systeem beëindigt de app als een taak een uitzondering gegenereert en die uitzondering niet wordt opgevangen door de taak, een van de vervolgtaken of de hoofdapplicatie.

  • Een vervolg op basis van een taak wordt altijd uitgevoerd; het maakt niet uit of de antecedent-taak is voltooid, een uitzondering heeft veroorzaakt of is geannuleerd. Een vervolg op basis van een waarde wordt niet uitgevoerd als de antecedent-taak wordt gegenereerd of geannuleerd.

  • Omdat vervolgen op basis van taken altijd worden uitgevoerd, kunt u overwegen of u een vervolg op basis van een taak wilt toevoegen aan het einde van de vervolgketen. Zo kunt u garanderen dat uw code alle uitzonderingen bekijkt.

  • De runtime genereert concurrency::task_canceled wanneer u concurrency::task::get aanroept en die taak wordt geannuleerd.

  • De runtime beheert geen uitzonderingen voor lichtgewicht taken en agents.

In dit document

Taken en vervolgen

In deze sectie wordt beschreven hoe de runtime uitzonderingen verwerkt die worden gegenereerd door gelijktijdigheid::taakobjecten en hun vervolgen. Zie Taakparallelisme voor meer informatie over het taak- en vervolgmodel.

Wanneer u een uitzondering genereert in de hoofdtekst van een werkfunctie die u doorgeeft aan een task object, slaat de runtime die uitzondering op en verzendt deze naar de context die concurrentie::task::get of concurrentie::task::wait aanroept. In het document Taakparallelisme worden de vervolgbewerkingen op basis van taken en waarden beschreven, maar om samen te vatten, neemt een vervolg op basis van waarde een parameter van het type T en een vervolg op basis van een taak een parameter van het type task<T>. Als een taak een uitzondering werpt en een of meer op waarden gebaseerde vervolgbewerkingen heeft, worden deze vervolgbewerkingen niet ingepland om te worden uitgevoerd. In het volgende voorbeeld ziet u dit gedrag:

// 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.
*/

Met een vervolg op basis van een taak kunt u elke uitzondering afhandelen die wordt gegenereerd door de antecedent-taak. Een vervolg op basis van een taak wordt altijd uitgevoerd; het maakt niet uit of de taak is voltooid, een uitzondering heeft veroorzaakt of is geannuleerd. Wanneer een taak een uitzondering genereert, worden de taakgebaseerde vervolgbewerkingen gepland zodat ze uitgevoerd kunnen worden. In het volgende voorbeeld ziet u een taak die altijd wordt gegooid. De taak heeft twee vervolgen; een is op waarde gebaseerd en de andere is op taken gebaseerd. De taakgebaseerde uitzondering wordt altijd uitgevoerd en kan daarom de uitzondering vangen die wordt opgeworpen door de voorafgaande taak. Wanneer het voorbeeld wacht tot beide vervolgen zijn voltooid, wordt de uitzondering opnieuw gegenereerd omdat de taak-uitzondering altijd wordt gegenereerd wanneer task::get of task::wait wordt aangeroepen.

// 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.
*/

We raden u aan om taakgebaseerde voortzettingen te gebruiken om uitzonderingen op te vangen die u kunt verwerken. Omdat vervolgen op basis van taken altijd worden uitgevoerd, kunt u overwegen of u een vervolg op basis van een taak wilt toevoegen aan het einde van de vervolgketen. Zo kunt u garanderen dat uw code alle uitzonderingen bekijkt. In het volgende voorbeeld ziet u een vervolgketen op basis van een basiswaarde. De derde taak in de keten genereert en daarom worden eventuele vervolgen op basis van waarden die volgen, niet uitgevoerd. De uiteindelijke voortzetting is echter gebaseerd op taken en wordt daarom altijd uitgevoerd. Met deze laatste voortzetting wordt de uitzondering verwerkt die wordt gegenereerd door de derde taak.

We raden u aan de meest specifieke uitzonderingen op te vangen die u kunt. U kunt deze uiteindelijke taakgerichte vervolgstap weglaten indien u geen specifieke uitzonderingen hebt om op te vangen. Elke uitzondering blijft onverwerkt en kan de app beëindigen.

// 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 task-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>
*/

Aanbeveling

U kunt de concurrency::task_completion_event::set_exception methode gebruiken om een uitzondering te koppelen aan een taakvoltooiingsgebeurtenis. In het document taakparallelisme wordt de gelijktijdigheid::task_completion_event klasse uitgebreider beschreven.

gelijktijdigheid::task_canceled is een belangrijk runtime-uitzonderingstype dat betrekking heeft op task. De runtime wordt uitgevoerd wanneer u aanroept task_canceledtask::get en die taak wordt geannuleerd. (Retourneert daarentegen task::waittask_status::geannuleerd en gooit geen uitzondering.) U kunt deze uitzondering opvangen en afhandelen in een taakgebaseerd vervolg of wanneer u task::get aanroept. Zie Annulering in de PPL voor meer informatie over het annuleren van taken.

Waarschuwing

Gooi nooit task_canceled in uw code. Roep concurrency::cancel_current_task aan in plaats daarvan.

Het runtime-systeem beëindigt de app als een taak een uitzondering gegenereert en die uitzondering niet wordt opgevangen door de taak, een van de vervolgtaken of de hoofdapplicatie. Als uw toepassing vastloopt, kunt u Visual Studio zo configureren dat deze wordt onderbroken wanneer er C++-uitzonderingen worden gegenereerd. Nadat u de locatie van de niet-verwerkte uitzondering hebt vastgesteld, gebruikt u een taakgebaseerde voortzetting om deze te behandelen.

In de sectie Uitzonderingen die door de runtime in dit document worden gegenereerd, wordt beschreven hoe u met runtime-uitzonderingen in meer detail kunt werken.

[Boven]

Taakgroepen en parallelle algoritmen

In deze sectie wordt beschreven hoe de runtime uitzonderingen verwerkt die worden gegenereerd door taakgroepen. Deze sectie is ook van toepassing op parallelle algoritmen, zoals gelijktijdigheid::p arallel_for, omdat deze algoritmen zijn gebaseerd op taakgroepen.

Waarschuwing

Zorg ervoor dat u begrijpt welke effecten uitzonderingen hebben op afhankelijke taken. Voor aanbevolen procedures voor het gebruik van uitzonderingsafhandeling met taken of parallelle algoritmen, raadpleegt u de sectie Uitleg over hoe annulering en uitzonderingsafhandeling invloed hebben op objectvernietiging in de aanbevolen procedures in het onderwerp Parallelle patronenbibliotheek.

Zie Taakparallellisme voor meer informatie over taakgroepen. Zie Parallelle algoritmen voor meer informatie over parallelle algoritmen.

Wanneer u een uitzondering genereert in de hoofdtekst van een werkfunctie die u doorgeeft aan een gelijktijdigheid::task_group of gelijktijdigheid::structured_task_group object, slaat de runtime deze uitzondering op en verplaatst deze naar de context die gelijktijdigheid::task_group::wait, gelijktijdigheid::structured_task_group::wait, gelijktijdigheid::task_group::run_and_wait of gelijktijdigheid::structured_task_group::run_and_wait aanroept. De runtime stopt ook alle actieve taken die zich in de taakgroep bevinden (inclusief taken in onderliggende taakgroepen) en verwijdert alle taken die nog niet zijn gestart.

In het volgende voorbeeld ziet u de basisstructuur van een werkfunctie die een uitzondering genereert. In het voorbeeld wordt een task_group object gebruikt om de waarden van twee point objecten parallel af te drukken. De print_point werkfunctie drukt de waarden van een point object af op de console. De werkfunctie genereert een uitzondering als de invoerwaarde is NULL. De runtime slaat deze uitzondering op en leidt deze naar de context die task_group::wait aanroept.

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

In dit voorbeeld wordt de volgende uitvoer geproduceerd.

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

Zie voor een volledig voorbeeld waarin uitzonderingsafhandeling in een taakgroep wordt gebruikt, Hoe: Uitzonderingsafhandeling gebruiken om een parallelle lus te verbreken.

[Boven]

Uitzonderingen die door de runtime worden gegenereerd

Een uitzondering kan het gevolg zijn van een aanroep naar de runtime. De meeste exceptietypen, met uitzondering van concurrency::task_canceled en concurrency::operation_timed_out, geven een programmeerfout aan. Deze fouten zijn doorgaans onherstelbaar en moeten daarom niet worden opgevangen of verwerkt door toepassingscode. We raden u aan om alleen onherstelbare fouten in uw toepassingscode te ondervangen of af te handelen wanneer u programmeerfouten moet diagnosticeren. Het begrijpen van de uitzonderingstypen die door de runtime zijn gedefinieerd, kan u echter helpen bij het diagnosticeren van programmeerfouten.

Het mechanisme voor het afhandelen van uitzonderingen is hetzelfde voor uitzonderingen die worden gegenereerd door de runtime als uitzonderingen die worden gegenereerd door werkfuncties. De concurrency::receive-functie gooit bijvoorbeeld een operation_timed_out-uitzondering wanneer geen bericht binnen de opgegeven tijdsperiode wordt ontvangen. Als receive een uitzondering genereert in een werkfunctie die je doorgeeft aan een taakgroep, slaat de runtime die uitzondering op en verzendt deze naar de context die task_group::wait, structured_task_group::wait, task_group::run_and_wait of structured_task_group::run_and_wait aanroept.

In het volgende voorbeeld wordt de concurrency::parallel_invoke-functionaliteit gebruikt om twee taken parallel te laten uitvoeren. De eerste taak wacht vijf seconden en verzendt vervolgens een bericht naar een berichtbuffer. De tweede taak gebruikt de receive functie om drie seconden te wachten om een bericht van dezelfde berichtbuffer te ontvangen. De receive functie werpt operation_timed_out als het de boodschap niet binnen de tijdsperiode ontvangt.

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

In dit voorbeeld wordt de volgende uitvoer geproduceerd.

The operation timed out.

Als u abnormale beëindiging van uw toepassing wilt voorkomen, moet u ervoor zorgen dat uw code uitzonderingen afhandelt wanneer deze aanroept in de runtime. Handel ook uitzonderingen af wanneer u externe code aanroept die gebruikmaakt van de Gelijktijdigheidsruntime, bijvoorbeeld een bibliotheek van derden.

[Boven]

Meerdere uitzonderingen

Als een taak of parallel algoritme meerdere uitzonderingen ontvangt, geeft de runtime slechts één van deze uitzonderingen door naar de aanroepende context. De runtime garandeert niet welke uitzondering het doorstuurt.

In het volgende voorbeeld wordt het parallel_for algoritme gebruikt om getallen naar de console af te drukken. Er wordt een uitzondering gegenereerd als de invoerwaarde kleiner is dan een minimumwaarde of groter is dan een bepaalde maximumwaarde. In dit voorbeeld kunnen meerdere werkfuncties een uitzondering genereren.

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

Hieronder ziet u voorbeelduitvoer voor dit voorbeeld.

8293104567Caught exception: -5: the value is less than the minimum.

[Boven]

Annulering

Niet alle uitzonderingen geven een fout aan. Een zoekalgoritmen kunnen bijvoorbeeld uitzonderingsafhandeling gebruiken om de bijbehorende taak te stoppen wanneer het resultaat wordt gevonden. Zie Annulering in de PPL voor meer informatie over het gebruik van annuleringsmechanismen in uw code.

[Boven]

Lichtgewicht taken

Een lichtgewicht taak is een taak die u rechtstreeks vanuit een concurrency::Scheduler object plant. De overhead is lager voor lichtgewicht taken dan voor gewone taken. De runtime vangt echter geen uitzonderingen op die worden opgeworpen door lichtgewicht taken. In plaats daarvan wordt de uitzondering gevangen door de niet-verwerkte uitzonderingshandler, waardoor het proces standaard wordt beëindigd. Gebruik daarom een geschikt mechanisme voor foutafhandeling in uw toepassing. Zie Task Scheduler voor meer informatie over lichtgewicht taken.

[Boven]

Asynchrone agents

Net als lichtgewicht taken beheert de runtime geen uitzonderingen die worden gegenereerd door asynchrone agents.

In het volgende voorbeeld ziet u één manier om uitzonderingen in een klasse af te handelen die is afgeleid van gelijktijdigheid::agent. In dit voorbeeld wordt de points_agent klasse gedefinieerd. De points_agent::run methode leest point objecten uit de berichtbuffer en drukt deze af op de console. De run methode genereert een uitzondering als deze een NULL aanwijzer ontvangt.

De run methode omringt alle werkzaamheden in een try-catch blok. In catch het blok wordt de uitzondering opgeslagen in een berichtbuffer. De toepassing controleert of de agent een fout heeft aangetroffen door na te gaan of deze buffer is gelezen nadat de agent zijn taak heeft voltooid.

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

In dit voorbeeld wordt de volgende uitvoer geproduceerd.

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

Omdat het try-catch blok buiten de while lus bestaat, wordt de verwerking beëindigd wanneer de agent de eerste fout tegenkomt. Als het try-catch blok zich in de while lus zou bevinden, zou de agent doorgaan nadat er een fout is opgetreden.

In dit voorbeeld worden uitzonderingen opgeslagen in een berichtbuffer, zodat een ander onderdeel de agent kan controleren op fouten terwijl deze wordt uitgevoerd. In dit voorbeeld wordt een concurrency::single_assignment-object gebruikt om de fout op te slaan. In het geval dat een agent meerdere uitzonderingen verwerkt, slaat de single_assignment klasse alleen het eerste bericht op dat eraan wordt doorgegeven. Als u alleen de laatste uitzondering wilt opslaan, gebruikt u concurrency::overwrite_buffer. Als u alle uitzonderingen wilt opslaan, gebruikt u de gelijktijdigheid::unbounded_buffer-klasse . Zie Asynchrone berichtblokken voor meer informatie over deze berichtblokken.

Voor meer informatie over asynchrone agents, zie Asynchrone Agents.

[Boven]

Samenvatting

[Boven]

Zie ook

Gelijktijdigheidsruntime
Parallelle uitvoering van taken
Parallelle algoritmen
Annulering in de PPL
Taakplanner
Asynchrone agenten