From: Volker Bartheld on
Hi!

I just stumbled over this section in the wxW-docu: "[...] However there is
no built in method to send messages to the worker threads and you will
need to use the available synchronization classes to implement the
solution which suits your needs yourself. In particular, please note that
it is not enough to derive your class from wxThread and wxEvtHandler to
send messages to it: in fact, this does not work at all.
[...]"
(http://www.wxwidgets.org/manuals/stable/wx_wxthreadoverview.html#wxthreadoverview)

Hmmm. Strange that I didn't spot this earlier... And even stranger that the
sample below which does exactly this (deriving a class WorkerThread :
public wxEvtHandler, public wxThread) seems to happily update its counter
m_uiCount through OnReset(), On Thread() and the wxEVT_THREAD event it
receives - at least with the wxW versions (2.7x and 2.8x) and platforms I
have tested.

So what's the background behind this statement "does not work at all" and
how am I meant to do it right?

While we are at it: Suppose I want to send some data from a worker to the
main thread that is neither int nor wxString. Probably a struct or a void*
to some dynamically allocated memory. What's the recommended way to
announce where the data can be found?

struct tFOO { /* ... */ };
wxCommandEvent evt(wxEVT_THREAD, wxID_ANY);
tFOO* pFoo=new tFOO;
evt.SetInt((int)pFoo);
m_pParent->AddPendingEvent(evt);

Or rather some fancy string formatting and wxCommandEvent::SetString()? Or
abusing Set/GetClientData() even if the docu states the this "Returns
[the] client data pointer for a listbox or choice selection event"?

Thanks in advance for any help!

Volker


<threadsample.cpp>
#include "wx/wx.h"
#include <wx/thread.h>
#include <wx/event.h>
#include <wx/statusbr.h>
#include <wx/frame.h>
#include <wx/string.h>
#include <wx/utils.h>

enum { Minimal_Quit=wxID_EXIT, Minimal_Stop_Thread=wxID_HIGHEST+1, Minimal_Reset_Thread };
DECLARE_EVENT_TYPE(wxEVT_THREAD, -1)
DEFINE_EVENT_TYPE(wxEVT_THREAD)

class WorkerThread : public wxEvtHandler, public wxThread
{
public:
WorkerThread(wxFrame* pParent) : m_pParent(pParent) { Create(); }
virtual ExitCode Entry()
{
wxThread::ExitCode exitcode=0;
wxCommandEvent evt(wxEVT_THREAD, wxID_ANY);
try
{
for(m_uiCount=100; m_uiCount; --m_uiCount)
{
if(TestDestroy()) throw (int)0; // the 0 will make it into the event's int and inform the main thread about successfully processing wxThread::Delete()
evt.SetInt(m_uiCount); m_pParent->AddPendingEvent(evt); // send updated counter value to main thread
wxSleep(1);
} // for(unsigned int uiCount=3600; uiCount; --uiCount)
} // try
catch(int i) { evt.SetInt(i); m_pParent->AddPendingEvent(evt); }
wxSleep(5);
return (wxThread::ExitCode)0;
} // virtual wxThread::ExitCode Entry()
void OnThread(wxCommandEvent& event) { m_uiCount=event.GetInt(); }
private:
DECLARE_EVENT_TABLE()
unsigned int m_uiCount;
wxFrame* m_pParent;
};
BEGIN_EVENT_TABLE(WorkerThread, wxEvtHandler)
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, WorkerThread::OnThread)
END_EVENT_TABLE()

class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title) : wxFrame(NULL, wxID_ANY, title), m_pThread(NULL)
{
wxMenu *fileMenu=new wxMenu;
fileMenu->Append(Minimal_Reset_Thread, _T("&Reset thread\tAlt-N"), _T("Resets the worker thread"));
fileMenu->Append(Minimal_Stop_Thread, _T("&Stop thread\tAlt-S"), _T("Stops the worker thread"));
fileMenu->Append(Minimal_Quit, _T("E&xit\tAlt-X"), _T("Exit this program"));
wxMenuBar *menuBar=new wxMenuBar();
menuBar->Append(fileMenu, _T("&File"));
SetMenuBar(menuBar);
CreateStatusBar(); SetStatusText(_T("Welcome to wxWidgets!"));
m_pThread=new WorkerThread(this);
m_pThread->Run();
}
void OnStop(wxCommandEvent& WXUNUSED(event))
{
if(m_pThread)
{
wxMenu* pMenu=GetMenuBar()->GetMenu(0);
pMenu->FindItem(Minimal_Reset_Thread)->Enable(false);
pMenu->FindItem(Minimal_Stop_Thread)->Enable(false);
SetStatusText(_T("Stopping thread..."));
m_pThread->Delete(); m_pThread=NULL; // Delete() blocks until worker thread calls TestDestroy()
}
}
void OnReset(wxCommandEvent& WXUNUSED(event))
{
wxCommandEvent evt(wxEVT_THREAD, wxID_ANY); evt.SetInt(42);
m_pThread->AddPendingEvent(evt);
}
void OnQuit(wxCommandEvent& WXUNUSED(event)) { Close(); }
void OnThread(wxCommandEvent& event)
{
if(!event.GetInt())
{
SetStatusText(_T("Thread stopped."));
m_pThread=NULL;
Close();
return;
}
SetStatusText(wxString::Format(_T("Counting %i..."), event.GetInt()));
}
void OnClose(wxCloseEvent& WXUNUSED(event))
{
wxCommandEvent e;
OnStop(e);
Destroy();
}
private:
WorkerThread* m_pThread;
DECLARE_EVENT_TABLE()
}; // class MyFrame : public wxFrame
BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(Minimal_Quit, MyFrame::OnQuit)
EVT_MENU(Minimal_Reset_Thread, MyFrame::OnReset)
EVT_MENU(Minimal_Stop_Thread, MyFrame::OnStop)
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, MyFrame::OnThread)
EVT_CLOSE(MyFrame::OnClose)
END_EVENT_TABLE()

class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
if(!wxApp::OnInit()) return false;
MyFrame *frame=new MyFrame(_T("Minimal wxWidgets App"));
frame->Show(true);
return true;
}
}; // class MyApp : public wxApp
IMPLEMENT_APP(MyApp)
</threadsample.cpp>


--
mailto: V B A R T H E L D at G M X dot D E
From: Vadim Zeitlin on
On Tue, 5 Jun 2007 13:55:04 +0200 Volker Bartheld <dr_versaeg(a)freenet.de> wrote:

VB> I just stumbled over this section in the wxW-docu: "[...] However there is
VB> no built in method to send messages to the worker threads and you will
VB> need to use the available synchronization classes to implement the
VB> solution which suits your needs yourself. In particular, please note that
VB> it is not enough to derive your class from wxThread and wxEvtHandler to
VB> send messages to it: in fact, this does not work at all.
VB> [...]"
VB> (http://www.wxwidgets.org/manuals/stable/wx_wxthreadoverview.html#wxthreadoverview)
VB>
VB> Hmmm. Strange that I didn't spot this earlier... And even stranger that the
VB> sample below which does exactly this (deriving a class WorkerThread :
VB> public wxEvtHandler, public wxThread) seems to happily update its counter
VB> m_uiCount through OnReset(), On Thread() and the wxEVT_THREAD event it
VB> receives - at least with the wxW versions (2.7x and 2.8x) and platforms I
VB> have tested.
VB>
VB> So what's the background behind this statement "does not work at all" and
VB> how am I meant to do it right?

At the very least by protecting m_uiCount from concurrent access. Your
OnThread() is currently called in the main thread context so it's not
surprising that it works -- the fact that it's in wxThread-derived class
doesn't mean anything.

Regards,
VZ

--
TT-Solutions: wxWidgets consultancy and technical support
http://www.tt-solutions.com/


---------------------------------------------------------------------
To unsubscribe, e-mail: wx-users-unsubscribe(a)lists.wxwidgets.org
For additional commands, e-mail: wx-users-help(a)lists.wxwidgets.org