传输异常在线程间

Visual C++ 支持 传输 从一个线程 中 的 异常 到另一个。 传输异常在不同的线程可捕捉一个线程中的异常并提交异常会引发。 例如,可以使用此功能编写一个多线程应用程序的主线程处理其辅助线程引发的所有异常。 传输异常主要想创建并行编程库或系统开发人员很有用。 若要实现传输异常, Visual C++ 提供 exception_ptr 类型和 current_exception、 rethrow_exception和 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);
}

参数

Parameter

说明

unspecified

用于实现 exception_ptr 类型的未指定的内部类。

p

引用异常的 exception_ptr 对象。

E

表示异常的类。

e

参数 E 类的实例。

返回值

current_exception 函数返回引用异常当前正在进行的 exception_ptr 对象。 如果没有异常进度中,函数返回未关联有任何异常的 exception_ptr 对象。

copy_exception 函数返回引用 e 参数指定的异常 exception_ptr 对象。

备注

方案

假设您要创建能将处理每个变量的工作负荷的应用程序。 为了实现此目标,您设计初始,主线程创建许多辅助线程的多线程应用程序,则需要以使该工作。 辅助线程帮助主线程管理资源,平衡负载和提高吞吐量。 通过将工作,该多线程应用程序更好地单线程应用程序的执行。

但是,因此,如果次要线程引发了异常,则需要主线程处理它。 这是因为,您希望应用程序处理异常以保持一致,辅助线程的数目,统一的方式。

解决方案

处理以上方案中, C++ 标准支持传输异常在线程之间。 如果次要线程引发异常,该异常成为 当前异常。 通过对实际的比方,当前异常 在航班中被视为。 它将引发直到异常处理程序捕获它的返回当前异常是在航班中从时间。

辅助线程在 exception_ptr 对象可以捕获 catch 的当前异常块,然后调用 current_exception 函数存储异常。 exception_ptr 对象必须可用于辅助线程和对主线程。 例如, exception_ptr 对象可以是访问由 mutex 控件的全局变量。 异常 表示的术语 传输 一个线程中的异常可以转换为可由其他线程访问的窗体。

接下来,主线程调用 rethrow_exception 功能,提取然后引发从 exception_ptr 对象的异常。 引发异常时,它成为在主线程的当前异常。 即异常出现则源自主线程。

最后,主线程可以捕获 catch 的当前异常然后阻塞处理它或引发它向较高的异常处理程序。 或者,主线程可以忽略异常并允许进程结束。

大多数应用程序不必传输异常在线程之间。 但是,在中,因为系统可以分为辅助线程、处理器或内核中,的工作此功能很有用在并行计算的系统。 在并行计算环境中,唯一,专用线程上处理辅助线程的所有异常,并可以提供一致的异常处理模型到所有应用程序。

有关 C++ 标准委员会建议的更多信息,请搜索 Internet 为单据数字 N2179,标题为 “语言为传输异常支持在线程间”。

异常处理模型和编译器选项

应用程序的异常处理模型确定它是否可以捕获和传输异常。 Visual C++ 支持可以处理 C++ 异常、结构化异常处理 (SEH)异常和公共语言运行时异常的三个 (CLR)模型。 使用 /EH/clr 编译器选项指定应用程序的异常处理模型。

编译器选项和过程的语句只有以下组合可能传输异常。 其他组合不捕获异常,也不捕获,但是不能传输异常。

  • /EHa 编译器选项和 catch语句可以传输 SEH 和 C++ 异常。

  • /EHa/EHs/EHsc 编译器选项和 catch语句可以传输 C++ 异常。

  • /CLR/CLR:pure 编译器选项和 catch语句可以传输 C++ 异常。 /CLR 编译器选项提示 /EHa 选项的规范。 请注意编译器不支持传输托管异常。 这是因为,托管异常,从 System.Exception 类派生,已使用公共 languange 运行时的功能,可以将线程之间的对象。

    安全说明安全说明

    建议您指定 /EHsc 编译器选项并捕捉只有 C++ 异常。在显示自己在安全威胁,如果使用 /EHa/CLR 编译器选项和一个 catch 语句与省略号 异常声明 (catch(...))。您可能希望使用 catch 语句捕获一些特定异常。但是, catch(...) 语句捕捉所有 C++ 和 SEH 异常,包括应是致命的意外结果。如果忽略或意外的异常处理不当,恶意代码可以使用该机会损坏程序的安全性。

用法

使用 exception_ptr类型,以下各节描述如何传输异常和 current_exception、 rethrow_exception和 copy_exception 功能。

Dd293602.collapse_all(zh-cn,VS.110).gifexception_ptr 类型

使用一 exception_ptr 对象引用当前异常或用户指定的异常的实例。 Microsoft 实现,异常。 EXCEPTION_RECORD 结构表示。 每 exception_ptr 对象包含一个异常引用指向 EXCEPTION_RECORD 结构的副本的表示异常的字段。

在声明 exception_ptr 变量时,变量不关联有任何异常。 即异常引用字段为空。 这样 exception_ptr 对象调用空白 exception_ptr。

使用 current_exception 或 copy_exception 函数分配异常。 exception_ptr 对象。 当将异常。 exception_ptr 变量时,变量的异常引用字段指向异常的副本。 如果具有复制异常的内存不足异常,引用字段指向 std:: bad_alloc 异常的副本。 如果 current_exception 或 copy_exception 函数不能为任何其他原因导致复制异常,函数调用 terminate (CRT) 函数退出当前进程。

尽管其名称, exception_ptr 对象本身不属于指针。 它不指针语义,并且不能用于指针成员访问 (->) 或间接寻址 (*) 运算符。 exception_ptr 对象没有公共数据成员或成员函数。

比较:

可以使用相等 (==) 和不相等 (!=) 运算符比较两 exception_ptr 对象。 运算符不比较二进制值 (位组合表示异常) 的 EXCEPTION_RECORD 结构。 相反,运算符比较异常中的地址引用 exception_ptr 对象的字段。 因此, null exception_ptr 和将空值进行比较为相等。

Dd293602.collapse_all(zh-cn,VS.110).gifcurrent_exception 功能

调用 catch 的 current_exception 功能块。 如果在航班中,并 catch 块可以捕获异常, current_exception 函数返回引用异常的 exception_ptr 对象。 否则,该函数返回空 exception_ptr 对象。

详细信息:

current_exception 函数获取位于航班中的异常 catch 语句指定是否 异常声明 语句。

,如果不重新引发异常,当前异常的析构函数调用 catch 块的结尾处。 但是,因此,即使您调用析构函数的current_exception 功能,函数返回引用当前异常的 exception_ptr 对象。

连续调用 current_exception 函数返回引用当前异常的不同复制的 exception_ptr 对象。 因此,对象进行比较时,不相等,因为它们是引用不同的副本,因此,即使复制具有相同的二进制值。

SEH 异常:

如果使用 /EHa 编译器选项,则可以捕获她在 c. C++ catch 的异常块。 current_exception 函数返回引用 SEH 异常的 exception_ptr 对象。 调用,则它与传输的 exception_ptr 对象作为其参数,并 rethrow_exception 函数引发的异常。

,如果调用它在她 __finally 终止处理程序、 __except 异常处理程序或 __except 筛选器表达式, current_exception 函数返回空 exception_ptr 。

一个传输的异常不支持嵌套异常。 嵌套异常,则会出现另一将引发异常,中出现异常处理时。 如果可以捕获嵌套异常, EXCEPTION_RECORD.ExceptionRecord 数据成员指向的 EXCEPTION_RECORD 结构链描述关联的异常。 current_exception 功能不支持嵌套异常,因为它返回 ExceptionRecord 数据成员零的 exception_ptr 对象。

如果可以捕获 SEH 异常,必须管理在 EXCEPTION_RECORD.ExceptionInformation 数据成员数组的所有指针引用的内存。 您必须确保中存在相应的 exception_ptr 对象的生存期内有效的,因此,内存被释放,当 exception_ptr 对象删除过程。

可以使用传输异常函数一起使用结构化异常 (SE)转换器功能。 如果 SEH 异常转换为 c. C++ 异常,引用转换的异常而不是原始的 SEH 异常的 current_exception 函数返回 exception_ptr 。 rethrow_exception 函数随后引发转换的异常,而不是原始异常。 有关 SE 转换器功能的更多信息,请参见 _set_se_translator

Dd293602.collapse_all(zh-cn,VS.110).gifrethrow_exception 功能

在 exception_ptr 对象后存储所捕获的异常,主线程可以处理对象。 在主线程上,请使用 exception_ptr 对象时调用 rethrow_exception 函数作为其参数。 rethrow_exception 功能 exception_ptr 从对象提取异常然后引发异常在主线程中。 如果 rethrow_exception 功能的 p 参数为空 exception_ptr,该函数引发 std:: bad_exception

提取的异常现在位于主线程的当前异常,因此,您可以处理,则应引发其他异常。 如果捕获异常,您可以立即处理它或使用 throw 语句将其发送到较高的异常处理程序。 否则,不执行任何操作并允许默认系统异常处理程序会终止进程。

Dd293602.collapse_all(zh-cn,VS.110).gifcopy_exception 功能

copy_exception 函数采用类的实例,因为引用实例的参数然后返回 exception_ptr 。 通常,指定一 异常类 对象作为参数传递给 copy_exception 功能,不过,所有类对象可以是参数。

调用 copy_exception 函数引发 c. 只有等效,其捕获在 catch 块,然后调用 current_exception 函数返回引用异常的 exception_ptr 对象。 copy_exception 功能的 Microsoft 实现与引发和捕获异常有效。

应用程序通常不需要 copy_exception 功能,因此,我们不鼓励其用法。

示例

下面的示例传输标准 C++ 异常和自定义 C++ 异常从一个线程到另一个。

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

要求

**标题:**exception

请参见

参考

异常处理在Visual C++

EXCEPTION_RECORD 结构

处理程序语法

/EH(异常处理模型)

/clr(公共语言运行时编译)