简体   繁体   English

C ++:跨线程的错误处理问题

[英]C++: Error handling problem across threads

In general I use exceptions to deal with errors, however the problem I have here is that the error applies to a different thread than the one that caused it. 一般来说,我使用异常来处理错误,但是我在这里遇到的问题是错误适用于不同于导致错误的线程。

Basicly the window has its own thread, and the direct3d device must be created and reset by the same thread that created the window. 基本上,窗口有自己的线程,并且必须由创建窗口的同一线程创建并重置direct3d设备。 However creating the device may fail, so I need to throw an exception in the thread that was trying to create the instance, not the window code 但是,创建设备可能会失败,因此我需要在尝试创建实例的线程中抛出异常,而不是窗口代码

The callback function: 回调函数:

void Callback(HWND hwnd, boost::function<void(HWND,LPARAM)> call, LPARAM lParam)
{
    //Make our stack allocated function object into a heap allocated one
    boost::function<void(HWND,LPARAM)> *callH = new boost::function<void(HWND,LPARAM)>(call);
    //send a message with a pointer to our function object in the WPARAM
    PostMessage(hwnd, WM_CALLBACK, (unsigned)callH, lParam);
}
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    //check for our custom message
    if(msg == WM_CALLBACK)
    {
        //retreive the function pointer from the WPARAM
        boost::function<void(HWND,LPARAM)> *callH = (boost::function<void(HWND,LPARAM)>*)wParam;
        //call it
        (*callH)(hwnd,lParam);
        //delete our heap allocated function object
        delete callH;
        return 0;
    }
    else
        //if there was nothing relevant to us, call the old message procedure
        return CallWindowProc(hooked[hwnd], hwnd, msg, wParam, lParam);
}
//std::map<HWND, WNDPROC> hooked;

The code that requests the window thread to create the device 请求窗口线程创建设备的代码

Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
    window->AddRef();
    HWND hwnd = *((HWND*)window->GetHandle());
    CallbackHook(hwnd);
    Callback(hwnd, boost::bind(&Graphics::create, this), 0);

    while(!started)Sleep(100);
}
void Graphics::create()
{
...code that may throw various exceptions
    started = true;
}

So basically I need the exceptions being thrown by the create() method to be caught by the exception handelers where the Graphics object was created which is another thread. 所以基本上我需要create()方法抛出的异常被创建Graphics对象的异常handelers捕获,这是另一个线程。

Perhaps you can wrap the call inside another function, using Boost.Exception . 也许你可以使用Boost.Exception将调用包装在另一个函数中。

The following code does not work though, but you'll probably get the idea. 以下代码不起作用,但您可能会得到这个想法。

class Context
{
public:
    Context(HWND hwnd, const boost::function<void(HWND,LPARAM)>& f, LPARAM lParam)
    {
        // TODO: reroute call through Wrapper function.
    }

    void wait()
    {
        mutex::scoped_lock l(m);
        while (!finished)
        {
            c.wait(l);
        }
        if (ex)
            rethrow_exception(ex);
    }

private:
    void Wrapper()
    {
        try
        {
            f(/*params*/);
        }
        catch (...)
        {
            ex = current_exception();
        }
        mutex::scoped_lock l(m);
        finished = true;
        c.notify_all();
    }

    boost::function<void(HWND,LPARAM)> f;
    exception_ptr ex;
    bool finished;
    mutex m;
    condition c;
};

void Callback(HWND hwnd, const boost::function<void(HWND,LPARAM)>& f, LPARAM lParam)
{
    Context ctx(hwnd, f, lParam);

    ctx.wait();
}

I see you call PostMessage from Callback then actively wait using a loop and Sleep . 我看到你从Callback调用PostMessage然后主动等待使用循环和Sleep Replace PostMessage by SendMessage , which will wait for the message processing to complete. SendMessage替换PostMessage ,它将等待消息处理完成。 That way the processor-intensive loop is gone. 这样,处理器密集型循环就消失了。 Moreover, you can use the return code of the SendMessage call (from the WM_CALLBACK arm of HookProc ) to pass error information. 此外,您可以使用SendMessage调用的返回代码(来自WM_CALLBACKHookProc )来传递错误信息。 Either use simple numeric codes (eg non-zero means error) or pass exception objects (in this case you'll have to allocate then on the heap from your exception handler, you can't pass the parameter directly as it is stack-allocated). 使用简单的数字代码(例如非零意味着错误)或传递异常对象(在这种情况下,你必须在异常处理程序的堆上分配,你不能直接传递参数,因为它是堆栈分配的)。

Last, using the synchronous SendMessage instead of the async PostMessage means you no longer have to build an intermediary callH function object from the HookProc , as you can pass the stack allocated call parameter instead. 最后,使用同步SendMessage而不是async PostMessage意味着您不再需要从HookProc构建中间callH函数对象,因为您可以传递堆栈分配的call参数。 This results in much simpler code. 这导致更简单的代码。

For example: 例如:

void Callback(HWND hwnd, boost::function<void(HWND,LPARAM)> call, LPARAM lParam)
{
    //send a message with a pointer to our function object in the WPARAM
    if (SendMessage(hwnd, WM_CALLBACK, &call, lParam))
    {
        // error handling.
    }
}
LRESULT CALLBACK HookProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    //check for our custom message
    if(msg == WM_CALLBACK)
    {
        //retreive the function pointer from the WPARAM
        boost::function<void(HWND,LPARAM)> *callH = (boost::function<void(HWND,LPARAM)>*)wParam;
        //call it, catching exceptions in the process.
        try 
        {
            (*callH)(hwnd,lParam);
            return 0;
        } 
        catch (...) 
        {
            return 1;
        }
    }
    else
        //if there was nothing relevant to us, call the old message procedure
        return CallWindowProc(hooked[hwnd], hwnd, msg, wParam, lParam);
}
//std::map<HWND, WNDPROC> hooked;

And: 和:

Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
    window->AddRef();
    HWND hwnd = *((HWND*)window->GetHandle());
    CallbackHook(hwnd);
    Callback(hwnd, boost::bind(&Graphics::create, this), 0);

    // No need to wait, as SendMessage is synchronous.
}
void Graphics::create()
{
...code that may throw various exceptions
    started = true;
}

A simple solution would be to catch the exception in the thread that thrown it (or check the parameters before calling the function to decide if an exception would be thrown) and then pass the exception via a static variable tot the main thread where the main thread can throw it. 一个简单的解决方案是在抛出它的线程中捕获异常(或者在调用函数之前检查参数以确定是否会抛出异常)然后通过静态变量传递异常主线程所在的主线程可以抛出它。

I would add a check after the WaitForSingleObject to discover if an exception is waiting to be thrown and in case it does - throw it. 我会在WaitForSingleObject之后添加一个检查,以发现是否等待抛出异常,以防它抛出 - 抛出它。

Well, the answer is that you can't. 嗯,答案是你做不到。 A viable alternative would be to do only necessary work that can't fail, like extractiong value from the parameters, in your callback function, and call the create() function in your main thread. 一个可行的替代方案是只进行不会失败的必要工作,比如从参数中提取值,在回调函数中,并在主线程中调用create()函数。

Also, you shouldn't busy wait for the creation, use a Semaphore, or a Mutex & Event etc. 此外,您不应该忙等待创建,使用信号量,或互斥和事件等。

Graphics::Graphics(IWindow *_window, Size2<unsigned> _size)
:lost(false), reset(false), refCnt(0), backCol(0xFF000000),
started(false), exited(false),
window(_window), size(_size)
{
    window->AddRef();
    HWND hwnd = *((HWND*)window->GetHandle());
    semaphore = CreateSemaphore(NULL, 0, 1, NULL);
    if (semaphore == NULL) throw whatever;
    CallbackHook(hwnd);
    Callback(hwnd, boost::bind(&Graphics::create, this), 0);

    WaitForSingleObject(semaphore);
    create();

    CloseHandle(semaphore); /* you won't need it */
}
void Graphics::create()
{
...code that may throw various exceptions
}
void Graphics::create_callback()
{
    ReleaseSemaphore(semaphore, 1, NULL);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM