0

I have a GUI program with wxwidgets. When a Button is clicked, a new thread is supposed to start and run, so that it doesn't block the UI. From the thread, a function is called that takes a callback function as argument. When I run the program without threading (hence blocked UI), there's no Problem. But when I use boost:thread as i do in the code below, the thread does not run properly. The program stops in the callback function CallBack() when it tries to set the member variable m_intValue. This is succesful without a seperate thread, but hangs when I put it in a separate thread. Any thoughts? I have tried to simplify the code so that it's more readable. Please excuse if it won't compile like this. I'm just asking, maybe someone knows right away where the problem is!

Code

class MainFrame : public wxFrame
{

 protected:
 boost:thread m_workerThread;
 m_intValue;

 WorkerThread(Foo& afoo, Bar& aBar)
 {
  afoo.doSmth(aBar, boost::bind(&MainFrame::CallBack, this, _1, _2, _3)
 }

 void OnButtonClick()
 {
  Foo oFoo;
  Bar oBar;
  m_workerThread = boost::thread(&MainFrame::WorkerThread, this, boost::ref(oFoo), boost::ref(oBar));
 }

 void CallBack(int arg, int arg1, int arg2 )
 {
  m_intValue = arg;
 }


};
tzippy
  • 6,458
  • 30
  • 82
  • 151

2 Answers2

1

The code as shown is completely MT-unsafe as there is no synchronization between main thread (which presumably accesses m_intValue as well) and the worker thread, in context of which your callback is executing. This can be fixed by using a critical section or a mutex to protect access to it.

If your real code uses any wxWidgets UI functionality from the callback, it's not going to work at all however, as mentioned by @ravenspoint, wxWidgets UI can only be used from the main thread. The only solution to this problem is to execute the code doing any UI from the main thread.

And the best, because very simple, way to do it is to use CallAfter(), e.g.

void WorkerThread()
{
    ...
    wxGetApp().CallAfter(Callback, arg1, arg2);
}

If you are using C++11 a very useful variant is to avoid defining Callback() at all and just do

    wxGetApp().CallAfter([=]() { m_intValue = arg; });

Now this is perfectly safe as the assignment is done in the main thread.

VZ.
  • 21,740
  • 3
  • 39
  • 42
0

I am guessing that it is problematical to have WorkerThread and CallBack as methods of your MainFrame. wxWidgets does not like running GUI code in a worker thread. While the posted code looks harmless, I wonder what you have in the real code ... ?

I suggest moving WorkerThread and CallBack into a new clas that does NOT inherit from any wx class - this will prevent you inadvertently executing GUI code from your worker thread and causing havoc.

ravenspoint
  • 19,093
  • 6
  • 57
  • 103