C ++:boost :: exception的跨线程异常处理问题

时间:2009-03-20 17:08:53

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

基本上,我有一种情况,一个线程抛出异常,而另一个线程需要处理。我试图用boost异常来做这个,但是在这个行的某个地方,异常会丢失它的类型,因此不会被catch块捕获。

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

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

First-chance exception at 0x776b42eb ...: fllib::exception::Error at memory location 0x0019e590..  
First-chance exception at 0x776b42eb ...: [rethrow] at memory location 0x00000000..  
First-chance exception at 0x776b42eb ...: boost::exception_detail::clone_impl<boost::unknown_exception> at memory location 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...");
}

4 个答案:

答案 0 :(得分:2)

确保来自线程A的每次抛出都使用boost :: enable_current_exception包装器。请参阅this

  

“除非enable_current_exception是   当时调用异常对象   用于throw-expression,an   尝试使用复制它   current_exception可能会返回一个   exception_ptr,指的是   unknown_exception的实例。“

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

此外,除非您使用_set_se_translator来调用包装器并将它们包装在异常对象中,否则这对于除零等结构化异常不起作用。

答案 1 :(得分:1)

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

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

  • virtual Base * Clone();克隆异常并在堆上返回一个新异常。这就解决了当离开catch块时origenall one被销毁的事实。
  • virtual void ThrowAndDelete();这使得堆分配的异常对象的本地副本,删除堆一个(以防止内存泄漏),然后抛出堆栈副本。这具有抛出作为最派生类型的优势。

这意味着我现在可以做到:

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 :: varity的异常,因为它们似乎没有上述效果的方法......但95%以上的例外情况并非如此事先处理的是我自己的课程,而那些没有的课程几乎肯定会被处理掉......

答案 2 :(得分:1)

  1. 使用BOOST_THROW_EXCEPTION抛出异常。

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

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

  4. 见www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html。

答案 3 :(得分:0)

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

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