Rafting des exceptions entre les threads
Visual C++ prend en charge le rafting une exception d'un thread à un autre.Le rafting des exceptions vous permet d'intercepter une exception dans un thread puis pour que l'exception sembler levé dans un thread différent.Par exemple, vous pouvez utiliser cette fonction pour écrire une application multithread où le thread principal gère toutes les exceptions levées par ses threads secondaires.Le rafting des exceptions est utile principalement aux développeurs qui créent des bibliothèques de programmation parallèle ou systèmes.Pour implémenter le rafting des exceptions, Visual C++ utilise le type d' exception_ptr et l' current_exception, rethrow_exception, et les fonctions d' copy_exception .
namespace std
{
typedef unspecified exception_ptr;
exception_ptr current_exception();
void rethrow_exception(exception_ptr p);
template<class E>
exception_ptr copy_exception(E e);
}
Paramètres
Paramètre |
Description |
---|---|
unspecified |
une classe interne non spécifiée qui est utilisée pour implémenter le type d' exception_ptr . |
p |
un objet d' exception_ptr qui référence une exception. |
E |
une classe qui représente une exception. |
e |
une instance de la classe d' E de paramètre. |
Valeur de retour
La fonction d' current_exception retourne un objet d' exception_ptr qui référence l'exception qui est actuellement en cours.Si aucune exception n'est en cours, la fonction retourne un objet d' exception_ptr qui n'est associé à aucune exception.
La fonction d' copy_exception retourne un objet d' exception_ptr qui référence l'exception spécifié par le paramètre d' e .
Notes
Scénario
Supposons que vous vouliez créer une application qui peut mettre à l'échelle pour gérer une quantité de travail variable.Pour atteindre cet objectif, vous concevez une application multithread où une initiale, thread principal crée autant de threads secondaires telle qu'elle doit afin d'exécuter un travail.Les threads secondaires aident le thread principal à gérer des ressources, équilibrer des surcharges, et améliorer le débit.En répartissant le travail, l'application multithread est plus performante qu'une application monothread.
Toutefois, si un thread secondaire lève une exception, vous souhaitez que le thread principal de la gérer.C'est parce que votre application doit gérer des exceptions de façon cohérente et unifiée indépendamment du nombre de threads secondaires.
Solution
Pour gérer l'exemple précédent, la norme C++ prend en charge le rafting une exception entre les threads.si un thread secondaire lève une exception, cette exception devient l' exception actuelle.Par on au monde réel, l'exception actuelle est dite en vol.L'exception actuelle a lieu en vol de temps où elle est levée jusqu'à ce que le gestionnaire d'exceptions que les intercepter il retourne.
Le thread secondaire peut intercepter l'exception actuelle dans un bloc d' catch , puis appelle la fonction d' current_exception pour stocker l'exception dans un objet d' exception_ptr .L'objet d' exception_ptr doit être disponible au thread secondaire et au thread principal.par exemple, l'objet d' exception_ptr peut être une variable globale dont l'accès est contrôlé par un mutex.Le transport term qu' une exception signifie qu'une exception dans un thread peut être convertie en un formulaire accessible par un autre thread.
Ensuite, le thread principal appelle la fonction d' rethrow_exception , qui récupère puis lève l'exception à l'objet d' exception_ptr .Lorsque l'exception est levée, elle devient l'exception actuelle dans le thread principal.Autrement dit, l'exception peut provenir du thread principal.
Enfin, le thread principal peut intercepter l'exception actuelle dans un bloc puis un processus d' catch il ou la lever à un gestionnaire d'exceptions de niveau supérieur.Ou, le thread principal peut ignorer l'exception et permettre un processus à la fin.
La plupart des applications n'ont pas le rafting des exceptions entre les threads.Toutefois, cette fonctionnalité est utile dans un système informatique parallèle car le système peut diviser le travail parmi les threads secondaires, les processeurs, ou les cœurs.Dans un environnement informatique parallèle, un thread unique et dédié peut gérer toutes les exceptions dans les threads secondaires et peut présenter un modèle cohérent de gestion des exceptions à n'importe quelle application.
Pour plus d'informations sur la proposition de comité de normalisation C++, recherchez Internet pour le numéro de document N2179, intitulé « prise en charge de transporter des exceptions entre les threads ».
modèles et options du compilateur de gestion des exceptions
Le modèle de gestion des exceptions de votre application détermine s'il peut intercepter et d'effectuer une exception.Visual C++ prend en charge trois modèles qui peuvent gérer des exceptions C++, la gestion des exceptions (SEH) structurée des exceptions, et les exceptions (CLR) du common langage runtime.Utilisez les options du compilateur de /EH et de /clr de spécifier le modèle de gestion des exceptions de votre application.
Seule la combinaison suivante des options du compilateur et des instructions de programmation peut effectuer une exception.d'autres combinaisons ne peuvent pas intercepter des exceptions, ou peuvent intercepter mais ne peuvent pas transporter des exceptions.
L'option du compilateur pour /EHa et l'instruction d' catchpeuvent effectuer fois SEH et des exceptions C++.
/EHa, /EHs, et les options du compilateur pour /EHsc et l'instruction d' catchpeuvent indiquer des exceptions C++.
L'option du compilateur pour /CLR ou d' /CLR:pure et l'instruction d' catchpeuvent indiquer des exceptions C++.Les options du compilateur pour /CLR impliquent la spécification de l'option d' /EHa .Notez que le compilateur ne prend pas en charge le rafting des exceptions managées.Cela est dû au fait que les exceptions managées, qui sont dérivés de la classe de System.Exception , sont déjà des objets dont vous pouvez vous déplacer entre les threads à l'aide de les fonctionnalités du common runtime de languange.
Note de sécurité Nous vous conseillons de spécifier l'option du compilateur pour /EHsc et interceptez uniquement des exceptions C++.Vous vous exposez à une menace pour la sécurité si vous utilisez l'option du compilateur pour /EHa ou d' /CLR et une instruction d' catch avec une déclaration d'exception de sélection (catch(...)).Vous souhaitez peut-être que d'utiliser l'instruction d' catch pour capturer des exceptions spécifiques.Toutefois, l'instruction d' catch(...) capture tous les C++ et la fois des exceptions SEH de, notamment les inattendues qui doivent être mortelles.Si vous ignorez ou traitez mal une exception inattendue, le code malveillant peut utiliser cette possibilité de fragiliser la sécurité de votre programme.
Utilisation
Les sections suivantes décrivent comment effectuer des exceptions à l'aide de le type d' exception_ptr, et current_exception, rethrow_exception, et les fonctions d' copy_exception .
type d'exception_ptr
Utilisez un objet d' exception_ptr pour référencer l'exception actuelle ou une instance d'une exception spécifiée par l'utilisateur.dans l'implémentation Microsoft, une exception est représentée par une structure d' EXCEPTION_RECORD .Chaque objet d' exception_ptr inclut un champ de référence d'exception qui pointe vers une copie d' EXCEPTION_RECORD structure qui représente l'exception.
Lorsque vous déclarez une variable d' exception_ptr , la variable n'est associée à aucune exception.Autrement dit, son champ de référence de l'exception est NULL.un tel objet d' exception_ptr est appelé un exception_ptr null.
Utilisez la fonction d' current_exception ou d' copy_exception pour assigner une exception à un objet d' exception_ptr .Lorsque vous assignez une exception à une variable d' exception_ptr , les points de champ de référence de l'exception de la variable par une copie de l'exception.Si la mémoire insuffisante pour copier l'exception, les points de champ de référence d'exception à une copie d'une exception de type : : bad_alloc .Si la fonction d' current_exception ou d' copy_exception ne peut pas copier l'exception pour une autre raison, la fonction appelle la fonction de terminate (CRT) de quitter le processus actuel.
en dépit de son nom, un objet d' exception_ptr n'est pas lui-même un pointeur.Il n'obéit pas la sémantique de pointeur et ne peut pas être utilisé avec les opérateurs membres d'accès (->) ou d'indirection du pointeur (*).l'objet d' exception_ptr n'a aucune donnée membre ou fonction membre publique.
comparaisons :
vous pouvez utiliser les opérateurs d'égal (==) et de non-égal (!=) pour comparer deux objets d' exception_ptr .Les opérateurs ne sont pas la valeur binaire (modèle binaire) des structures d' EXCEPTION_RECORD qui représentent les exceptions.À la place, les opérateurs comparent les adresses dans le champ de référence d'exception les objets d' exception_ptr .Par conséquent, exception_ptr null et la valeur NULL sont considérés comme égaux.
fonction de current_exception
appelez la fonction d' current_exception dans un bloc d' catch .Si une exception est en vol et le bloc d' catch peut intercepter l'exception, la fonction d' current_exception retourne un objet d' exception_ptr qui référence l'exception.Sinon, la fonction retourne un objet null d' exception_ptr .
Détails :
La fonction d' current_exception capture l'exception indépendamment est en vol si l'instruction d' catch spécifie une instruction de déclaration d'exception .
Le destructeur de l'exception actuelle est appelé à la fin de le bloc d' catch si vous n'avez pas à nouveau l'exception.Toutefois, même si vous appelez la fonction d'current_exception dans le destructeur, la fonction d'retourne un objet d' exception_ptr qui référence l'exception actuelle.
Les appels répétés à la fonction d' current_exception retournent des objets d' exception_ptr qui font référence à des copies de l'exception actuelle.En conséquence, les objets sont considérés comme égaux car ils font référence à des copies, bien que les copies aient la même valeur binaire.
Fois SEH exceptions :
Si vous utilisez l'option du compilateur pour /EHa , vous pouvez intercepter l'exception dans le bloc C++ catch .La fonction d' current_exception retourne un objet d' exception_ptr qui référence exception.Et la fonction d' rethrow_exception la lève une exception si vous appelez avec l'objet d' exception_ptr fourni parcomme argument.
La fonction d' current_exception retourne exception_ptr null si vous le est appelez dans le gestionnaire de terminaisons d' __finally , un gestionnaire d'exceptions d' __except , ou l'expression de filtre d' __except .
Une exception transportée ne prend pas en charge les exceptions imbriquées.Une exception imbriquée se produit si une autre exception est levée lorsqu'une exception est gérée.si vous interceptez une exception imbriquée, les points de donnée membre d' EXCEPTION_RECORD.ExceptionRecord à une chaîne des structures d' EXCEPTION_RECORD qui décrivent les exceptions associées.La fonction d' current_exception ne prend pas en charge les exceptions imbriquées car elle retourne un objet d' exception_ptr dont les données membres d' ExceptionRecord est mise à zéro.
Si vous interceptez exception, vous devez gérer la mémoire référencée par un pointeur dans le tableau des données membres d' EXCEPTION_RECORD.ExceptionInformation .Vous devez vérifier que la mémoire est valide pendant la durée de vie de l'objet correspondant d' exception_ptr , et que cette mémoire est libérée lorsque l'objet d' exception_ptr est supprimé.
Vous pouvez utiliser les fonctions de transcodage structurées (SE) d'exception avec la fonctionnalité d'exceptions de transport.Si une exception SEH est traduite en une exception C++, la fonction d' current_exception retourne exception_ptr qui référence l'exception traduite au lieu de l'original une exception SEH.la fonction d' rethrow_exception lève ultérieurement l'exception traduite, pas l'exception d'origine.Pour plus d'informations sur les fonctions de transcodage SE, consultez _set_se_translator.
fonction de rethrow_exception
Après avoir enregistré une exception interceptée dans un objet d' exception_ptr , le thread principal peut traiter l'objet.Dans votre thread principal, appelez la fonction d' rethrow_exception avec l'objet d' exception_ptr comme argument.La fonction d' rethrow_exception extrait l'exception à l'objet d' exception_ptr puis lève l'exception dans le contexte du thread principal.si le paramètre d' p de la fonction d' rethrow_exception est exception_ptrnull, la fonction lève type : : bad_exception.
L'exception récupérée est maintenant l'exception actuelle dans le thread principal, et vous pouvez le gérer comme n'importe quelle autre exception.Si vous interceptez l'exception, vous pouvez le gérer immédiatement ou utiliser une instruction d' throw pour l'envoyer à un gestionnaire d'exceptions de niveau supérieur.Sinon, ne faites rien et laissez le gestionnaire d'exceptions par défaut du système terminer votre processus.
fonction de copy_exception
La fonction d' copy_exception prend une instance d'une classe comme son argument puis retourne exception_ptr qui référence l'instance.Généralement, vous spécifiez un objet de classe d'exception comme argument à la fonction d' copy_exception , bien que n'importe quel objet de classe peut être l'argument.
Appeler la fonction d' copy_exception équivaut à lever une exception C++, à l'intercepter dans un bloc d' catch , puis d'appeler la fonction d' current_exception pour retourner un objet d' exception_ptr qui référence l'exception.L'implémentation Microsoft de la fonction d' copy_exception est plus efficace que la levée et l'interception d'une exception.
Une application ne requiert en général pas la fonction d' copy_exception , et nous décourageons son utilisation.
Exemple
L'exemple suivant fourni une exception C++ standard et une exception de le personnalisé C++ à un thread à un autre.
// transport_exception.cpp
// compile with: /EHsc /MD
#include <windows.h>
#include <stdio.h>
#include <exception>
#include <stdexcept>
using namespace std;
// Define thread-specific information.
#define THREADCOUNT 2
exception_ptr aException[THREADCOUNT];
int aArg[THREADCOUNT];
DWORD WINAPI ThrowExceptions( LPVOID );
// Specify a user-defined, custom exception.
// As a best practice, derive your exception
// directly or indirectly from std::exception.
class myException : public std::exception {
};
int main()
{
HANDLE aThread[THREADCOUNT];
DWORD ThreadID;
// Create secondary threads.
for( int i=0; i < THREADCOUNT; i++ )
{
aArg[i] = i;
aThread[i] = CreateThread(
NULL, // Default security attributes.
0, // Default stack size.
(LPTHREAD_START_ROUTINE) ThrowExceptions,
(LPVOID) &aArg[i], // Thread function argument.
0, // Default creation flags.
&ThreadID); // Receives thread identifier.
if( aThread[i] == NULL )
{
printf("CreateThread error: %d\n", GetLastError());
return -1;
}
}
// Wait for all threads to terminate.
WaitForMultipleObjects(THREADCOUNT, aThread, TRUE, INFINITE);
// Close thread handles.
for( int i=0; i < THREADCOUNT; i++ ) {
CloseHandle(aThread[i]);
}
// Rethrow and catch the transported exceptions.
for ( int i = 0; i < THREADCOUNT; i++ ) {
try {
if (aException[i] == NULL) {
printf("exception_ptr %d: No exception was transported.\n", i);
}
else {
rethrow_exception( aException[i] );
}
}
catch( const invalid_argument & ) {
printf("exception_ptr %d: Caught an invalid_argument exception.\n", i);
}
catch( const myException & ) {
printf("exception_ptr %d: Caught a myException exception.\n", i);
}
}
}
// Each thread throws an exception depending on its thread
// function argument, and then ends.
DWORD WINAPI ThrowExceptions( LPVOID lpParam )
{
int x = *((int*)lpParam);
if (x == 0) {
try {
// Standard C++ exception.
// This example explicitly throws invalid_argument exception.
// In practice, your application performs an operation that
// implicitly throws an exception.
throw invalid_argument("A C++ exception.");
}
catch ( const invalid_argument & ) {
aException[x] = current_exception();
}
}
else {
// User-defined exception.
aException[x] = copy_exception( myException() );
}
return TRUE;
}
Configuration requise
en-tête : <exception>
Voir aussi
Référence
Gestion des exceptions dans Visual C++
structure d'EXCEPTION_RECORD
syntaxe de gestionnaire