【问题标题】:C++: Error handling problem across threadsC++:跨线程的错误处理问题
【发布时间】:2010-10-02 04:41:00
【问题描述】:

一般来说,我使用异常来处理错误,但是我遇到的问题是错误适用于与导致它的线程不同的线程。

基本上,窗口有自己的线程,direct3d 设备必须由创建窗口的同一线程创建和重置。但是创建设备可能会失败,所以我需要在试图创建实例的线程中抛出异常,而不是窗口代码

回调函数:

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;

请求窗口线程创建设备的代码

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

所以基本上我需要 create() 方法抛出的异常被创建 Graphics 对象的异常处理程序捕获,这是另一个线程。

【问题讨论】:

    标签: c++ multithreading exception error-handling


    【解决方案1】:

    嗯,答案是你不能。一个可行的替代方案是只做不会失败的必要工作,例如在回调函数中从参数中提取值,然后在主线程中调用 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);
    }
    

    【讨论】:

    • 这是不可能的,创建一个 Direct3D 设备必须使用创建窗口的同一线程来完成,并且 99% 的时间是那些失败的线程。
    • 啊,所以。您应该将异常保存到某个变量,然后检查另一个线程中的变量。看看 dalle 的回答
    【解决方案2】:

    一个简单的解决方案是在抛出它的线程中捕获异常(或在调用函数之前检查参数以确定是否会抛出异常),然后通过静态变量将异常传递给主线程,其中主线程可以抛出它。

    我会在 WaitForSingleObject 之后添加一个检查,以发现是否有异常正在等待抛出,如果它确实存在 - 抛出它。

    【讨论】:

      【解决方案3】:

      我看到你从Callback 调用PostMessage,然后使用循环和Sleep 积极等待。将PostMessage 替换为SendMessage,这将等待消息处理完成。这样,处理器密集型循环就消失了。此外,您可以使用SendMessage 调用的返回码(来自HookProcWM_CALLBACK 分支)来传递错误信息。使用简单的数字代码(例如,非零表示错误)或传递异常对象(在这种情况下,您必须在异常处理程序的堆上分配,您不能直接传递参数,因为它是堆栈分配的)。

      最后,使用同步 SendMessage 而不是异步 PostMessage 意味着您不再需要从 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;
      

      还有:

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

      【讨论】:

        【解决方案4】:

        也许您可以使用 Boost.Exception 将调用封装在另一个函数中。

        虽然下面的代码不起作用,但你可能会明白。

        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();
        }
        

        【讨论】:

          【解决方案5】:

          【讨论】:

          • 答案的编写方式应使其免受链接腐烂的影响,即它们应包含实际答案,而不是重定向。
          猜你喜欢
          • 1970-01-01
          • 2016-05-07
          • 1970-01-01
          • 1970-01-01
          • 2011-12-18
          • 1970-01-01
          • 2012-01-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多