C++ C++;:跨线程错误处理问题
一般来说,我使用异常来处理错误,但是这里的问题是错误应用于不同的线程,而不是导致错误的线程 基本上,窗口有自己的线程,direct3d设备必须由创建窗口的同一线程创建和重置。但是,创建设备可能会失败,因此我需要在尝试创建实例的线程中抛出异常,而不是窗口代码 回调函数:C++ C++;:跨线程错误处理问题,c++,multithreading,exception,error-handling,C++,Multithreading,Exception,Error Handling,一般来说,我使用异常来处理错误,但是这里的问题是错误应用于不同的线程,而不是导致错误的线程 基本上,窗口有自己的线程,direct3d设备必须由创建窗口的同一线程创建和重置。但是,创建设备可能会失败,因此我需要在尝试创建实例的线程中抛出异常,而不是窗口代码 回调函数: void Callback(HWND hwnd, boost::function<void(HWND,LPARAM)> call, LPARAM lParam) { //Make our stack alloc
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;
无效回调(HWND-HWND,boost::函数调用,LPARAM-LPARAM)
{
//将堆栈分配的函数对象转换为堆分配的函数对象
boost::function*callH=新的boost::function(call);
//在WPARAM中发送一条带有指向函数对象指针的消息
PostMessage(hwnd,WM_CALLBACK,(unsigned)callH,lParam);
}
LRESULT回调HookProc(HWND HWND,UINT msg,WPARAM WPARAM,LPARAM LPARAM)
{
//检查我们的自定义消息
if(msg==WM\u回调)
{
//从WPARAM中检索函数指针
boost::function*callH=(boost::function*)wParam;
//叫它
(*callH)(hwnd、lParam);
//删除堆分配的函数对象
删除callH;
返回0;
}
其他的
//如果与我们无关,请调用旧的消息过程
返回CallWindowProc(钩住[hwnd]、hwnd、msg、wParam、lParam);
}
//标准::地图钩;
请求窗口线程创建设备的代码
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;
}
Graphics::Graphics(IWindow*\u窗口,大小2\u)
:丢失(false)、重置(false)、引用(0)、返回列(0xFF000000),
启动(错误)、退出(错误),
窗口(_窗口),大小(_大小)
{
窗口->添加参考();
HWND HWND=*((HWND*)窗口->获取句柄();
回调钩(hwnd);
回调(hwnd,boost::bind(&Graphics::create,this),0);
当(!开始)睡眠时(100);
}
void Graphics::create()
{
…可能引发各种异常的代码
开始=真;
}
所以基本上我需要create()方法抛出的异常被创建图形对象的异常处理程序捕获,这是另一个线程。答案是不能。一个可行的替代方法是,只在回调函数中执行不会失败的必要工作,比如从参数中提取值,然后在主线程中调用create()函数 此外,您不应该忙于等待创建、使用信号量或互斥量&事件等
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);
}
Graphics::Graphics(IWindow*\u窗口,大小2\u)
:丢失(false)、重置(false)、引用(0)、返回列(0xFF000000),
启动(错误)、退出(错误),
窗口(_窗口),大小(_大小)
{
窗口->添加参考();
HWND HWND=*((HWND*)窗口->获取句柄();
信号量=CreateSemaphore(NULL,0,1,NULL);
如果(信号量==NULL)抛出任何内容;
回调钩(hwnd);
回调(hwnd,boost::bind(&Graphics::create,this),0);
WaitForSingleObject(信号量);
创建();
CloseHandle(信号灯);/*您不需要它*/
}
void Graphics::create()
{
…可能引发各种异常的代码
}
void Graphics::create_callback()
{
释放信号量(信号量,1,空);
}
一个简单的解决方案是在抛出异常的线程中捕获异常(或者在调用函数之前检查参数以确定是否抛出异常),然后通过静态变量将异常传递给主线程,主线程可以在主线程中抛出异常
我会在WaitForSingleObject之后添加一个检查,以发现是否有异常正在等待抛出,如果有,则会抛出它。我看到您从
回调调用PostMessage
,然后使用循环和睡眠
主动等待。将PostMessage
替换为,这将等待消息处理完成。这样,处理器密集型循环就消失了。此外,您还可以使用SendMessage
调用的返回代码(来自HookProc
的WM_CALLBACK
臂)传递错误信息。使用简单的数字代码(例如,非零表示错误)或传递异常对象(在这种情况下,您必须从异常处理程序在堆上进行分配,不能直接传递参数,因为它是堆栈分配的)
最后,使用synchronousSendMessage
而不是asyncPostMessage
意味着您不再需要从HookProc
构建中间callH
函数对象,因为您可以传递堆栈分配的call
参数。这将导致更简单的代码
例如:
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;
无效回调(HWND-HWND,boost::函数调用,LPARAM-LPARAM)
{
//在WPARAM中发送一条带有指向函数对象指针的消息
if(发送消息(hwnd、WM_回调和调用、LPRAM))
{
//错误处理。
}
}
LRESULT回调HookProc(HWND HWND,UINT msg,WPARAM WPARAM,LPARAM LPARAM)
{
//检查我们的自定义消息
if(msg==WM\u回调)
{
//从WPARAM中检索函数指针
boost::function*callH=(boost::function*)wParam;
//调用它,捕获过程中的异常。
尝试
{
(*callH)(hwnd、lParam);
返回0;
}
捕获(…)
{
返回1;
}
}
其他的
//如果与我们无关,请调用旧的消息过程
返回CallWindowProc(钩住[hwnd]、hwnd、msg、wParam、lParam);
}
//标准::地图钩;
以及:
Graphics::Graphics(IWindow*\u窗口,大小2\u)
:丢失(false)、重置(false)、引用(0)、返回列(0xFF000000),
启动(错误)、退出(错误),
窗口(_窗口),大小(_大小)
{
窗口->添加参考();
HWND HWND=*((HWND*)窗口->获取句柄();
回调钩(hwnd);
回调(hwnd,boost::bind(&Graphics::create,this),0);
//无需等待,因为SendMessage是同步的。
}
void Graphics::create()
{
…可能会导致错误的代码
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;
}
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();
}