Partager via


Comment : convertir une boucle OpenMP qui a recours à la gestion des exceptions pour utiliser le runtime d'accès concurrentiel

Cet exemple montre comment convertir une boucle OpenMP parallel for qui exécute la gestion des exceptions afin d'utiliser le mécanisme de gestion des exceptions du runtime d'accès concurrentiel.

Dans OpenMP, une exception qui est levée dans une région parallèle doit être interceptée et gérée dans la même région, par le même thread. Une exception qui échappe à la région parallèle est interceptée par le gestionnaire d'exceptions non gérées, qui, par défaut, met fin au processus.

Dans le runtime d'accès concurrentiel, lorsque vous levez une exception dans le corps d'une fonction de travail que vous passez à un groupe de tâches, comme l'objet concurrency::task_group ou concurrency::structured_task_group, ou à un algorithme parallèle comme concurrency::parallel_for, le runtime stocke cette exception et la guide vers le contexte qui attend que le groupe ou l'algorithme de tâches se termine. Pour les groupes de tâches, le contexte d'attente est le contexte qui appelle concurrency::task_group::wait, concurrency::structured_task_group::wait, concurrency::task_group::run_and_wait ou concurrency::structured_task_group::run_and_wait. Pour un algorithme parallèle, le contexte d'attente est le contexte qui a appelé cet algorithme. Le runtime arrête également toutes les tâches actives qui sont dans le groupe de tâches, y compris celles des groupes de tâches enfants, et ignore toutes les tâches qui n'ont pas encore démarré.

Exemple

Cet exemple montre comment gérer des exceptions dans une région parallel OpenMP et dans un appel à parallel_for. La fonction do_work effectue une demande d'allocation de mémoire qui n'aboutit pas. Elle lève, par conséquent, une exception de type std::bad_alloc. Dans la version qui utilise OpenMP, le thread qui lève l'exception doit également l'intercepter. En d'autres termes, chaque itération d'une boucle parallèle OpenMP doit gérer l'exception. Dans la version qui utilise le runtime d'accès concurrentiel, le thread principal intercepte une exception qui est levée par un autre thread.

// concrt-omp-exceptions.cpp 
// compile with: /EHsc /openmp
#include <ppl.h>
#include <new>
#include <iostream>
#include <sstream>

using namespace concurrency;
using namespace std;

// Demonstrates a function that performs a memory allocation request  
// that does not succeed. 
void do_work(int)
{
   // The following memory allocation causes this function to  
   // throw std::bad_alloc. 
   char* ptr = new char[(~unsigned int((int)0)/2) - 1];

   // TODO: Assuming that the allocation succeeds, perform some work  
   // and free the allocated memory. 

   delete[] ptr;
}

// Demonstrates an OpenMP parallel loop that performs exception handling. 
void omp_exception_handling()
{
   #pragma omp parallel for  
      for(int i = 0; i < 10; i++) 
      {
         try {
            // Perform a unit of work.
            do_work(i);
         }
         catch (exception const& e) {
            // Print the error to the console.
            wstringstream ss;
            ss << L"An error of type '" << typeid(e).name() 
               << L"' occurred." << endl;
            wcout << ss.str();
         }
      }
}

// Demonstrates an Concurrency Runtime parallel loop that performs exception handling. 
void concrt_exception_handling()
{
   try {
      parallel_for(0, 10, [](int i) 
      {
         // Perform a unit of work.
         do_work(i);
      });
   }
   catch (exception const& e) {
      // Print the error to the console.
      wcout << L"An error of type '" << typeid(e).name() 
            << L"' occurred." << endl;
   }
}

int wmain()
{
   wcout << L"Using OpenMP..." << endl;
   omp_exception_handling();

   wcout << L"Using the Concurrency Runtime..." << endl;
   concrt_exception_handling();
}

Cet exemple génère la sortie suivante.

  

Dans la version de cet exemple qui utilise OpenMP, l'exception se produit dans chaque itération de boucle. Chaque itération gère l'exception. Dans la version qui utilise le runtime d'accès concurrentiel, le runtime stocke l'exception, arrête toutes les tâches actives, ignore toutes les tâches qui n'ont pas encore commencé et marshale l'exception au contexte qui appelle parallel_for.

Si vous avez besoin que la version qui utilise OpenMP se termine une fois que l'exception s'est produite, vous pouvez utiliser un indicateur booléen pour signaler à d'autres itérations de boucle que l'erreur s'est produite. Comme dans l'exemple de la rubrique Comment : convertir une boucle OpenMP qui a recours à l'annulation pour utiliser le runtime d'accès concurrentiel, les itérations de boucle ultérieures n'auront aucun effet si l'indicateur est défini. En revanche, si vous avez besoin que la boucle qui utilise le runtime d'accès concurrentiel se poursuive une fois que l'exception s'est produite, gérez l'exception dans le corps même de la boucle parallèle.

D'autres composants du runtime d'accès concurrentiel, tels que les agents asynchrones et les tâches légères, ne transportent pas d'exceptions. Au lieu de cela, les exceptions non gérées sont interceptées par le gestionnaire d'exceptions non gérées, qui, par défaut, met fin au processus. Pour plus d'informations sur la gestion des exceptions, consultez Gestion des exceptions dans le runtime d'accès concurrentiel.

Pour plus d'informations sur parallel_for et d'autres algorithmes parallèles, consultez Algorithmes parallèles.

Compilation du code

Copiez l'exemple de code et collez-le dans un projet Visual Studio, ou collez-le dans un fichier nommé concrt-omp-exceptions.cpp. Exécutez ensuite la commande suivante dans une fenêtre d'invite de commandes Visual Studio .

cl.exe /EHsc /openmp concrt-omp-exceptions.cpp

Voir aussi

Concepts

Migration d'OpenMP au runtime d'accès concurrentiel

Gestion des exceptions dans le runtime d'accès concurrentiel

Algorithmes parallèles