How to stop a joinable thread
Volker Bartheld
dr_versaeg at freenet.de
Fri Sep 14 03:35:58 PDT 2007
Hi Andy!
On Thu, 13 Sep 2007 17:33:26 +0100, Andy Robinson wrote:
> Now it says of Delete : "Note that while this could work on
> a joinable thread you simply should not call this routine on one as
> afterwards you may not be able to call wxThread::Wait to free the memory
> of that thread".
> So how should the main event thread cause TestDestroy to return true so
> the thread exits?
Actually, I've been through that hassle before and didn't come up with a
solution to use wxThread::Delete() in a way that it suited me (Delete seems
to block, at least on wxMSW, so there's no chance to kill a hung thread
that never calls TestDestroy and other shortcomings), so I decided to
implement some kind of FIFO structure that keeps track of the commands the
worker thread should execute - implementing message handlers in a worker
thread by deriving it from wxThread *and* wxEvtHandler might look like a
promising solution, but in fact it isn't. Events still get handled in the
main thread's context and this is surely not what you want.
In my code (see below, some error handling stuff stripped), OnJob() is
blocking due to a mutex in the FIFO buffer (eliminates the need for Sleep()
etc. when there's nothing to do) until the main thread calls
WorkerThread::AddJob() to start (a) new job(s). Shutting down the worker
thread is done via the eID_THREAD_EXIT command (I'm aware, that all pending
jobs will first be finished, but it's rather easy to implement something
that also sets a flag similar to what TestDestroy() is expecting). The
worker thread reports back to the main thread by adding pending events of
type EVT_THREAD into the command queue, so it is aware of error conditions,
completed jobs or whatever else.
Probably this helps you a bit (but be aware that the code might be buggy
since it was extracted from a quite large project - in that case
corrections are sure welcome) with getting your worker thread(s) do what
they should do.
Good luck,
Volker
This is the FIFO-class:
<fifo.h>
#include <vector>
#include <wx/arrstr.h>
#include <wx/thread.h>
class FIFO
{
public:
struct tFIFO
{
tFIFO() : m_cmd(wxID_ANY), m_pArg(NULL) {}
tFIFO(int cmd, const wxArrayString& args) : m_cmd(cmd), m_Args(args),
m_pArg(NULL) {}
tFIFO(int cmd, void* pArg) : m_cmd(cmd), m_pArg(pArg) {}
int m_cmd; wxArrayString m_Args; void* m_pArg;
};
FIFO();
~FIFO();
void Push(const tFIFO&);
tFIFO Pop();
size_t Stacksize();
protected:
std::vector<tFIFO> m_Jobs;
wxMutex m_MutexCondition, m_MutexQueue;
wxCondition* m_pCondition;
};
</fifo.h>
<fifo.cpp>
#include <vector>
#include <wx/thread.h>
#include "fifo.h"
FIFO::FIFO() : m_pCondition(NULL) { m_MutexCondition.Lock();
m_pCondition=new wxCondition(m_MutexCondition); }
FIFO::~FIFO() { if(m_pCondition) delete m_pCondition; }
void FIFO::Push(const tFIFO& job)
{
wxMutexLocker lock(m_MutexQueue);
m_Jobs.push_back(job);
m_pCondition->Signal();
}
FIFO::tFIFO FIFO::Pop()
{
tFIFO element;
if(m_Jobs.empty()) m_pCondition->Wait();
m_MutexQueue.Lock();
element=*(m_Jobs.begin());
m_Jobs.erase(m_Jobs.begin());
m_MutexQueue.Unlock();
return element;
}
size_t FIFO::Stacksize()
{
wxMutexLocker lock(m_MutexQueue);
return m_Jobs.size();
}
</fifo.cpp>
My Workerthread (which is a joinable one, probably for historic reasons)
looks like this:
<workerthread.h>
DECLARE_EVENT_TYPE(EVT_THREAD, -1)
class WorkerThread : public wxThread
{
public:
enum tTHREAD_COMMANDS { eID_THREAD_EXIT=wxID_EXIT,
eID_THREAD_NULL=wxID_HIGHEST+1,
eID_THREAD_STARTED };
WorkerThread(wxEvtHandler* pParent);
~WorkerThread();
void AddJob(tTHREAD_COMMANDS cmd, const wxArrayString& args);
bool IsIdle() {return (m_pFIFO->Stacksize()==0 &&
m_processingJob==false);}
private:
wxEvtHandler* m_pParent;
FIFO* m_pFIFO;
virtual void OnJob();
virtual wxThread::ExitCode Entry();
bool m_processingJob;
};
</workerthread.h>
<workerthread.cpp>
#include <wx/wx.h>
#include "fifo.h"
DEFINE_EVENT_TYPE(EVT_THREAD)
WorkerThread::WorkerThread(wxEvtHandler* pParent) :
wxThread(wxTHREAD_JOINABLE), m_pParent(pParent), m_pFIFO(NULL),
m_processingJob(false)
{
wxThread::Create();
}
WorkerThread::~WorkerThread() { delete m_pFIFO; }
wxThread::ExitCode WorkerThread::Entry()
{
int exitcode;
m_pFIFO=new FIFO();
wxCommandEvent evt_started(EVT_THREAD, eID_THREAD_STARTED);
m_pParent->AddPendingEvent(evt_started);
try
{
while(true) { OnJob(); }
}
catch(int& i) { exitcode=i; }
wxCommandEvent evt_exit(EVT_THREAD, exitcode);
m_pParent->AddPendingEvent(evt_exit);
return (wxThread::ExitCode)exitcode;
}
void WorkerThread::AddJob(tTHREAD_COMMANDS cmd, const wxArrayString& args)
{
m_pFIFO->Push(FIFO::tFIFO(cmd, args));
}
void WorkerThread::OnJob()
{
FIFO::tFIFO job=m_pFIFO->Pop();
m_processingJob=true;
switch(job.m_cmd)
{
case eID_THREAD_TERMINATE: throw (int)eID_THREAD_EXIT;
default: break;
}
m_processingJob=false;
}
--
mailto: V B A R T H E L D at G M X dot D E
More information about the wx-users
mailing list