【问题标题】:C++: Cross thread exception handling problem with boost::exceptionC ++:boost :: exception的跨线程异常处理问题
【发布时间】:2009-03-20 17:08:53
【问题描述】:

基本上,我遇到了一个线程抛出一个不同线程需要处理的异常的情况。我正在尝试使用 boost 异常来执行此操作,但是在某处异常会丢失其类型,因此不会被 catch 块捕获。

基本上,线程 B 想要做某事,但是由于各种原因,它必须与线程 A 一起完成(如果您想知道这些原因,请询问 MS 为什么必须由同一线程创建、重置和释放 Direct3d 9 设备)创建了窗口)。如果在执行这些操作时发生异常,线程 A 会捕获它,并将其传递回线程 B,然后根据需要重新抛出它以进行处理。问题是线程 B 中抛出的异常似乎与线程 A 中抛出的异常不同。:(

我的程序的调试输出,代码如下。

0x776b42eb 处的第一次机会异常 ...: fllib::exception::Error at memory location 0x0019e590.. 0x776b42eb 处的第一次机会异常 ...:在内存位置 0x00000000 处 [rethrow].. 0x776b42eb 处的第一次机会异常 ...: boost::exception_detail::clone_impl<:unknown_exception> 在内存位置 0x0019eed4..
//thread B
...
try
{
    SendCallback(hwnd, boost::bind(&Graphics::create, this));
}
catch(fllib::exception::Error &except)//example catch block, doesnt catch example exception
{
    ...handle exception...
}

void SendCallback(HWND hwnd, boost::function<void()> call)
{
    boost::exception_ptr *except_ptr = 
        (boost::exception_ptr*)SendMessage(hwnd, WM_CALLBACK, (unsigned)&call, SEND);
    if(except_ptr)//if an exception occurred, throw it in thread B's context
    {
        boost::exception_ptr except = *except_ptr;
        delete except_ptr;
        boost::rethrow_exception(except);
    }
}
//thread A
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    //check for our custom message
    if(msg == WM_CALLBACK)
    {
        if(lParam == POST)
        {
            ...
        }
        else
        {
            boost::function<void()> *call = (boost::function<void()>*)wParam;
            try
            {
                (*call)();
            }
            catch(...)
            {
                return (unsigned)new boost::exception_ptr(boost::current_exception());
            }
            return 0;
        }
    }
    else
    {
        ...
    }
}

void Graphics::create()
{
...code that may throw exceptions...
eg
    throw fllib::exception::Error(L"Test Exception...");
}

【问题讨论】:

  • @Neil Butterworth 为什么要删除部分 VS 调试输出????
  • 抱歉 - 我以为我刚刚更正了标题。
  • throw ... 在我提供的调试输出的情况下: throw fllib::exception::Error(L"Test exception...");
  • 可能需要尝试使用 throw boost::enable_current_exception(my_exception()); revergestudios.com/boost-exception/…
  • 好的,除了我不控制所有可以在该块中引发异常的代码......然后唯一的选择是创建大量的catch块来“转换”异常,例如catch( ExceptionType &except){boost::throw_exception(except);}catch(AnotherException &except){...

标签: c++ multithreading exception boost exception-handling


【解决方案1】:

确保线程 A 的每次抛出都使用 boost::enable_current_exception 包装器。见this

"除非 enable_current_exception 是 当时调用了一个异常对象 在 throw 表达式中使用, 尝试使用复制它 current_exception 可能会返回一个 exception_ptr 指的是 unknown_exception 的实例。”

为此,您可能需要捕获线程 A 中的所有异常,并在使用 throw boost::enable_current_exception(your_exception) 包装它们后重新抛出它们。

此外,除非您使用_set_se_translator 调用包装器并将它们包装在异常对象中,否则这不适用于结构化异常(如除以零)。

【讨论】:

  • “要做到这一点...”如果没有针对每种可能的异常类型的 catch 块,我该如何做到这一点?
  • 参见 www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html。
【解决方案2】:

我为自己的异常类型找到了解决方案。

我向所有异常类型添加了 2 个虚拟方法。

  • 虚拟基地*克隆();克隆异常并在堆上返回一个新异常。这绕过了 origenall 在离开 catch 块时被破坏的事实。
  • 虚拟无效 ThrowAndDelete();这会生成堆分配的异常对象的本地副本,删除堆(以防止内存泄漏),然后抛出堆栈副本。这具有将 throwing 作为派生最多的类型的优势。

这意味着我现在可以简单地做:

void SendCallback(HWND hwnd, boost::function<void()> call)
{
    fllib::exception::Base *except_ptr = 
        (fllib::exception::Base*)SendMessage(hwnd, WM_CALLBACK, (unsigned)&call, SEND);
    if(except_ptr) except_ptr->ThrowAndDelete();
}
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    //check for our custom message
    if(msg == WM_CALLBACK)
    {
        if(lParam == POST)
        {
            ...
        }
        else
        {
            boost::function<void()> *call = (boost::function<void()>*)wParam;
            try
            {
                (*call)();
            }
            catch(fllib::exception::Base &except)
            {
                return (unsigned)except.Clone();
            }
            return 0;
        }
    }
    else
    {
        ...
    }
}

我不确定如何处理 std:: 和 boost:: 各种异常,因为它们似乎没有上述效果的方法......但是 95% 以上的异常可能不是要事先处理的是我自己的课程,而那些没有处理的几乎肯定会不处理......

【讨论】:

    【解决方案3】:
    1. 使用 BOOST_THROW_EXCEPTION 抛出异常。

    2. 在线程中,catch(...),然后调用 boost::current_exception 将异常(无论它可能是什么)填充到 boost::exception_ptr 中。

    3. 将 exception_ptr 复制到主线程,然后调用 rethrow_exception。这将重新抛出 catch(...) 在线程中捕获的异常对象。

    请参阅 www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html。

    【讨论】:

      【解决方案4】:

      我认为你有问题,因为没有用 boost 方式抛出异常。

      你可以在钩子过程中捕获线程A中的具体异常,将其复制到新对象中,返回指针..
      在 SendCallback 中检查结果,如果结果存在则抛出(非 NULL)。
      但是你应该确保如果你没有异常,总是会返回 NULL。

      【讨论】:

      • 问题是我需要为每种异常类型设置一个 catch 块,这意味着至少所有 std::、boost:: 以及我的代码使用的所有异常类型?然后我如何将该指针返回到正确的类型,而不是抛出一个无法预料的 void* 异常?
      • 良好做法是让所有异常都派生自 std::exception。
      • 你可以做下一个 some_error_info* result = 0;捕获(常量第三方::异常& e){结果=新的your_own_error_type(e); } catch( /* boost 的异常 */ ) { // 做同样的事情 } catch( const std::exception& e) { // 做同样的事情 } catch( ... ) { result = new unknown_error(); } 返回结果;
      • 但这又回到了拥有 1000 的 execpt、dynamic_casts 和 throws ......我的异常是 std::exception -> fllib::exception::Base -> ...,我假设 boost 的也?问题是 1% 的代码实际上捕获了一个 std::exception,而作为最后手段的代码确实会很好地崩溃
      • 所以我需要一种方法来重新抛出原始类型...除了对每种可能的类型使用动态强制转换之外,我看不到任何恢复原始类型的方法...例如,如果(FileNotFound *e = dynamic_cast(except))throw *e;if(next...
      猜你喜欢
      • 1970-01-01
      • 2012-08-09
      • 2010-10-02
      • 1970-01-01
      • 2011-01-18
      • 2011-04-27
      • 1970-01-01
      • 2014-05-23
      • 1970-01-01
      相关资源
      最近更新 更多