I am a hobby programmer learning C++ and multi-threading, and getting started on my first thread-pool attempt.
I use Code::Blocks 20.3, wxWidgets 3.1.4, and MinGW 17.1 on a Windows 10 Pro computer.
I have tried several thread-pool examples, but all blocked the GUI.
I found an example shown in https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication
that uses detached threads in a pool. This should not block the GUI.
I have "restructured" the 1 file example to work in a test project (gui, app, main, thread-pool modules).
I placed the classes in their own file, and moved the "main" part to the Main.cpp in my test project and replaced the gui code with a separate class file.
The standard example works as expected.
In the example, strings are passed to the thread-pool and other strings back to the main thread.
I have been searching for main thread AddToQueue() to pass any function like e.g. aTask() (void, or returning something to the main thread) that executes in the thread-pool. My search was not successful :-(.
=== Simple Task to be executed in a thread ===
std::vector<wxString> wxThreadCom2Frame::aTask(wxString wsSomeString, int x)
{
std::vector<wxString> vTest{};
for(int i = 0; i < x; i++)
{
wxString wsTest{};
wsTest << wsSomeString << " [" << i << "]";
vTest.push_back(wsTest);
}
return vTest;
}
=== Or as alternative, pass the vector by reference
aTask(_T("Just some text"), 5, &vTest); // to be queued with AddJob
===
void wxThreadCom2Frame::aTask(wxString wsSomeString, int x, std::vector<wxString> *vTest)
{
for(int i = 0; i < x; i++)
{
wxString wsTest{};
wsTest << wsSomeString << " [" << i << "]\n";
vTest->push_back(wsTest);
}
}
===
I hope someone can help me understand how to do this.
This is the first step to what I actually like to achieve.
An 'extraction' function returns a structure of 20 tags from a music file (mp3, flac, etc).
The main 'collecting' function will call the 'extraction' function for each file (up to 7000) in a list and place it in the queue of the thread-pool.
The 'collecting' function returns a vector of structures to the main thread for further processing.
Regards, Ruud.
=== ThreadCom.cpp ===
/////////////////////////////////////////////////////////////////////////////
// https://wiki.wxwidgets.org/Inter-Thread_and_Inter-Process_communication //
/////////////////////////////////////////////////////////////////////////////
// Standard
#include <stdlib.h>
#include <assert.h>
#include <map>
#include <list>
// wxWidgets
#include <wx/frame.h>
#include <wx/thread.h>
#include <wx/menu.h>
#include <wx/app.h>
class tJOB
{
public:
enum tCOMMANDS // list of commands that are currently implemented
{
eID_THREAD_EXIT=wxID_EXIT, // thread should exit or wants to exit
eID_THREAD_NULL=wxID_HIGHEST+1, // dummy command
eID_THREAD_STARTED, // worker thread has started OK
eID_THREAD_JOB = ID_THREAD_JOB, // process normal job
eID_THREAD_JOBERR = ID_THREAD_JOBERR // process erroneous job after which thread likes to exit
}; // enum tCOMMANDS
tJOB() : m_cmd(eID_THREAD_NULL) {}
tJOB(tCOMMANDS cmd, const wxString& arg) : m_cmd(cmd), m_Arg(arg) {}
tCOMMANDS m_cmd; wxString m_Arg;
}; // class tJOB
class QUEUE
{
public:
enum tPRIORITY { eHIGHEST, eHIGHER, eNORMAL, eBELOW_NORMAL, eLOW, eIDLE }; // priority classes
QUEUE(wxEvtHandler* pParent) : m_pParent(pParent) {}
void AddJob(const tJOB& job, const tPRIORITY& priority=eNORMAL) // push a job with given priority class onto the FIFO
{
wxMutexLocker lock(m_MutexQueue); // lock the queue
m_Jobs.insert(std::make_pair(priority, job)); // insert the prioritized entry into the multimap
m_QueueCount.Post(); // new job has arrived: increment semaphore counter
} // void AddJob(const tJOB& job, const tPRIORITY& priority=eNORMAL)
tJOB Pop()
{
tJOB element;
m_QueueCount.Wait(); // wait for semaphore (=queue count to become positive)
m_MutexQueue.Lock(); // lock queue
element=(m_Jobs.begin())->second; // get the first entry from queue (higher priority classes come first)
m_Jobs.erase(m_Jobs.begin()); // erase it
m_MutexQueue.Unlock(); // unlock queue
return element; // return job entry
} // tJOB Pop()
void Report(const tJOB::tCOMMANDS& cmd, const wxString& sArg=wxEmptyString, int iArg=0) // report back to parent
{
wxCommandEvent evt(wxEVT_THREAD, cmd); // create command event object
evt.SetString(sArg); // associate string with it
evt.SetInt(iArg);
m_pParent->AddPendingEvent(evt); // and add it to parent's event queue
} // void Report(const tJOB::tCOMMANDS& cmd, const wxString& arg=wxEmptyString)
size_t Stacksize() // helper function to return no of pending jobs
{
wxMutexLocker lock(m_MutexQueue); // lock queue until the size has been read
return m_Jobs.size();
}
private:
wxEvtHandler* m_pParent;
std::multimap<tPRIORITY, tJOB> m_Jobs; // multimap to reflect prioritization: values with lower keys come first, newer values with same key are appended
wxMutex m_MutexQueue; // protects queue access
wxSemaphore m_QueueCount; // semaphore count reflects number of queued jobs
};
class WorkerThread : public wxThread
{
public:
WorkerThread(QUEUE* pQueue, int id=0) : m_pQueue(pQueue), m_ID(id) { assert(pQueue); wxThread::Create(); }
private:
QUEUE* m_pQueue;
int m_ID;
virtual wxThread::ExitCode Entry()
{
Sleep(1000); // sleep a while to simulate some time-consuming init procedure
tJOB::tCOMMANDS iErr;
m_pQueue->Report(tJOB::eID_THREAD_STARTED, wxEmptyString, m_ID); // tell main thread that worker thread has successfully started
try { while(true) OnJob(); } // this is the main loop: process jobs until a job handler throws
catch(tJOB::tCOMMANDS& i) { m_pQueue->Report(iErr=i, wxEmptyString, m_ID); } // catch return value from error condition
return (wxThread::ExitCode)iErr; // and return exit code
} // virtual wxThread::ExitCode Entry()
virtual void OnJob()
{
tJOB job=m_pQueue->Pop(); // pop a job from the queue. this will block the worker thread if queue is empty
switch(job.m_cmd)
{
case tJOB::eID_THREAD_EXIT: // thread should exit
Sleep(1000); // wait a while
throw tJOB::eID_THREAD_EXIT; // confirm exit command
case tJOB::eID_THREAD_JOB: // process a standard job
Sleep(2000);
m_pQueue->Report(tJOB::eID_THREAD_JOB, wxString::Format(wxT("Job #%s done."), job.m_Arg.c_str()), m_ID); // report successful completion
break;
case tJOB::eID_THREAD_JOBERR: // process a job that terminates with an error
m_pQueue->Report(tJOB::eID_THREAD_JOB, wxString::Format(wxT("Job #%s erroneous."), job.m_Arg.c_str()), m_ID);
Sleep(1000);
throw tJOB::eID_THREAD_EXIT; // report exit of worker thread
break;
case tJOB::eID_THREAD_NULL: // dummy command
default:
break; // default
} // switch(job.m_cmd)
} // virtual void OnJob()
}; // class WorkerThread : public wxThread
=== partial wxThreadCom2Main.cpp ===
void wxThreadCom2Frame::AddToQueue( wxCommandEvent& event )
{
int iJob=rand();
m_pQueue->AddJob(tJOB((tJOB::tCOMMANDS)event.GetId(), wxString::Format(wxT("%u"), iJob)));
SetStatusText(wxString::Format(wxT("Job #%i started."), iJob)); // just set the status text
}
void wxThreadCom2Frame::OnThread(wxCommandEvent& event) // handler for thread notifications
{
switch(event.GetId())
{
case tJOB::eID_THREAD_JOB:
// Get the returned vector and do something with it
SetStatusText(wxString::Format(wxT("[%i]: %s"), event.GetInt(), event.GetString().c_str())); // progress display
break;
case tJOB::eID_THREAD_EXIT:
SetStatusText(wxString::Format(wxT("[%i]: Stopped."), event.GetInt()));
m_Threads.remove(event.GetInt()); // thread has exited: remove thread ID from list
if(m_Threads.empty()) { EnableControls(false); } // disable some menu items if no more threads
break;
case tJOB::eID_THREAD_STARTED:
SetStatusText(wxString::Format(wxT("[%i]: Ready."), event.GetInt()));
EnableControls(true); // at least one thread successfully started: enable controls
break;
default:
event.Skip();
}
}
void wxThreadCom2Frame::EnableControls(bool bEnable) // en/dis-able Stop, Add Job, Add JobErr
{
wxMenu* pMenu=GetMenuBar()->GetMenu(0);
static const int MENUIDS[]={/*ID_START_THREAD, */ID_THREAD_EXIT, ID_THREAD_JOB, ID_THREAD_JOBERR};
for(unsigned int i=0; i<WXSIZEOF(MENUIDS); pMenu->Enable(MENUIDS[i++], bEnable));
}
===
Related
I'm writing an Audio class that holds an std::thread for refilling some buffers asynchronously. Say we call the main thread A and the background (class member) thread B. I'm using an std::mutex to block thread B whenever the sound is not playing, that way it doesn't run in the background when unnecessary and doesn't use excess CPU power. The mutex locked by thread A by default, so thread B is blocked, then when it's time to play the sound thread A unlocks the mutex and thread B runs (by locking then immediately unlocking it) in a loop.
The issue comes up when thread B sees that it's reached the end of the file. It can stop playback and clean up buffers and such, but it can't stop its own loop because thread B can't lock the mutex from thread A.
Here's the relevant code outline:
class Audio {
private:
// ...
std::thread Thread;
std::mutex PauseMutex; // mutex that blocks Thread, locked in constructor
void ThreadFunc(); // assigned to Thread in constructor
public:
// ...
void Play();
void Stop();
}
_
void Audio::ThreadFunc() {
// ... (include initial check of mutex here)
while (!this->EndThread) { // Thread-safe flag, only set when Audio is destructed
// ... Check and refill buffers as necessary, etc ...
if (EOF)
Stop();
// Attempt a lock, blocks thread if sound/music is not playing
this->PauseMutex.lock();
this->PauseMutex.unlock();
}
}
void Audio::Play() {
// ...
PauseMutex.unlock(); // unlock mutex so loop in ThreadFunc can start
}
void Audio::Stop() {
// ...
PauseMutex.lock(); // locks mutex to stop loop in ThreadFunc
// ^^ This is the issue here
}
In the above setup, when the background thread sees that it's reached EOF, it would call the class's Stop() function, which supposedly locks the mutex to stop the background thread. This doesn't work because the mutex would have to be locked by the main thread, not the background thread (in this example, it crashes in ThreadFunc because the background thread attempts a lock in its main loop after already locking in Stop()).
At this point the only thing I could think of would be to somehow have the background thread lock the mutex as if it was the main thread, giving the main thread ownership of the mutex... if that's even possible? Is there a way for a thread to transfer ownership of a mutex to another thread? Or is this a design flaw in the setup I've created? (If the latter, are there any rational workarounds?) Everything else in the class so far works just as designed.
I'm not going to even pretend to understand how your code is trying to do what it is doing. There is one thing, however, that is evident. You're trying to use a mutex for conveying some predicate state change, which is the wrong vehicle to drive on that freeway.
Predicate state change is handled by coupling three things:
Some predicate datum
A mutex to protect the predicate
A condition variable to convey possible change in predicate state.
The Goal
The goal in the below example is to demonstrate how a mutex, a condition variable, and predicate data are used in concert when controlling program flow across multiple threads. It shows examples of using both wait and wait_for condition variable functionality, as well as one way to run a member function as a thread proc.
Following is a simple Player class toggles between four possible states:
Stopped : The player is not playing, nor paused, nor quitting.
Playing : The player is playing
Paused : The player is paused, and will continue from whence it left off once it resumes Playing.
Quit : The player should stop what it is doing and terminate.
The predicate data is fairly obvious. the state member. It must be protected, which means it cannot be changed nor checked unless under the protection of the mutex. I've added to this a counter that simply increments during the course of maintaining the Playing state for some period of time. more specifically:
While Playing, each 200ms the counter increments, then dumps some data to the console.
While Paused, counter is not changed, but retains its last value while Playing. This means when resumed it will continue from where it left off.
When Stopped, the counter is reset to zero and a newline is injected into the console output. This means switching back to Playing will start the counter sequence all over again.
Setting the Quit state has no effect on counter, it will be going away along with everything else.
The Code
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <unistd.h>
using namespace std::chrono_literals;
struct Player
{
private:
std::mutex mtx;
std::condition_variable cv;
std::thread thr;
enum State
{
Stopped,
Paused,
Playing,
Quit
};
State state;
int counter;
void signal_state(State st)
{
std::unique_lock<std::mutex> lock(mtx);
if (st != state)
{
state = st;
cv.notify_one();
}
}
// main player monitor
void monitor()
{
std::unique_lock<std::mutex> lock(mtx);
bool bQuit = false;
while (!bQuit)
{
switch (state)
{
case Playing:
std::cout << ++counter << '.';
cv.wait_for(lock, 200ms, [this](){ return state != Playing; });
break;
case Stopped:
cv.wait(lock, [this]() { return state != Stopped; });
std::cout << '\n';
counter = 0;
break;
case Paused:
cv.wait(lock, [this]() { return state != Paused; });
break;
case Quit:
bQuit = true;
break;
}
}
}
public:
Player()
: state(Stopped)
, counter(0)
{
thr = std::thread(std::bind(&Player::monitor, this));
}
~Player()
{
quit();
thr.join();
}
void stop() { signal_state(Stopped); }
void play() { signal_state(Playing); }
void pause() { signal_state(Paused); }
void quit() { signal_state(Quit); }
};
int main()
{
Player player;
player.play();
sleep(3);
player.pause();
sleep(3);
player.play();
sleep(3);
player.stop();
sleep(3);
player.play();
sleep(3);
}
Output
I can't really demonstrate this. You'll have to run it and see how it works, and I invite you to toy with the states in main() as I have above. Do note, however, that once quit is invoked none of the other stated will be monitored. Setting the Quit state will shut down the monitor thread. For what its worth, a run of the above should look something like this:
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.
1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.
with the first set of numbers dumped in two groups (1..15, then 16..30), as a result of playing, then pausing, then playing again. Then a stop is issued, followed by another play for a period of ~3 seconds. After that, the object self-destructs, and in doing so, sets the Quit state, and waits for the monitor to terminate.
Summary
Hopefully you get something out of this. If you find yourself trying to manage predicate state by manually latching and releasing mutexes, changes are you need a condition-variable design patter to facilitate detecting those changes.
Hope you get something out of it.
class CtLockCS
{
public:
//--------------------------------------------------------------------------
CtLockCS() { ::InitializeCriticalSection(&m_cs); }
//--------------------------------------------------------------------------
~CtLockCS() { ::DeleteCriticalSection(&m_cs); }
//--------------------------------------------------------------------------
bool TryLock() { return ::TryEnterCriticalSection(&m_cs) == TRUE; }
//--------------------------------------------------------------------------
void Lock() { ::EnterCriticalSection(&m_cs); }
//--------------------------------------------------------------------------
void Unlock() { ::LeaveCriticalSection(&m_cs); }
//--------------------------------------------------------------------------
protected:
CRITICAL_SECTION m_cs;
};
///////////////////////////////////////////////////////////////////////////////
// class CtLockMX - using mutex
class CtLockMX
{
public:
//--------------------------------------------------------------------------
CtLockMX(const TCHAR* nameMutex = 0)
{ m_mx = ::CreateMutex(0, FALSE, nameMutex); }
//--------------------------------------------------------------------------
~CtLockMX()
{ if (m_mx) { ::CloseHandle(m_mx); m_mx = NULL; } }
//--------------------------------------------------------------------------
bool TryLock()
{ return m_mx ? (::WaitForSingleObject(m_mx, 0) == WAIT_OBJECT_0) : false; }
//--------------------------------------------------------------------------
void Lock()
{ if (m_mx) { ::WaitForSingleObject(m_mx, INFINITE); } }
//--------------------------------------------------------------------------
void Unlock()
{ if (m_mx) { ::ReleaseMutex(m_mx); } }
//--------------------------------------------------------------------------
protected:
HANDLE m_mx;
};
///////////////////////////////////////////////////////////////////////////////
// class CtLockSM - using semaphore
class CtLockSM
{
public:
//--------------------------------------------------------------------------
CtLockSM(int maxcnt) { m_sm = ::CreateSemaphore(0, maxcnt, maxcnt, 0); }
//--------------------------------------------------------------------------
~CtLockSM() { ::CloseHandle(m_sm); }
//--------------------------------------------------------------------------
bool TryLock() { return m_sm ? (::WaitForSingleObject(m_sm, 0) == WAIT_OBJECT_0) : false; }
//--------------------------------------------------------------------------
void Lock() { if (m_sm) { ::WaitForSingleObject(m_sm, INFINITE); } }
//--------------------------------------------------------------------------
void Unlock()
{
if (m_sm){
LONG prevcnt = 0;
::ReleaseSemaphore(m_sm, 1, &prevcnt);
}
}
//--------------------------------------------------------------------------
protected:
HANDLE m_sm;
};
CASE 1:
I am using wxThreads, I am creating threads using 2 for loops. I have a MyThreads class which is inherited from wxThread class. Also, every thread creates a wxThreadEvent before exit and sends data to main program. Main program executes DoThisWorkAfterThreadReturns() after every thread is finished. what I want to do is, all threads with level=0 can execute simultaneously. But before creating threads with level = 1, all level 0 threads should have finished their execution and DoThisWorkAfterThreadReturns() execution for all level 0 threads should also be finished. How should I do this using wxWidgets ?
for(level=0;level<n;level++)
{
for(int i=0;i<no;i++)
{
//threads in this loop can execute simultaneously.
MyThread *thread = new MyThread(this);
thread->create();
thread->run();
}
//wait till all threads for given level finish execution and execute
DoThisWorkAfterThreadReturns()
}
CASE 2:
If CASE 1 is not possible then can I do following ?
for(i=0;i<n;i++)
{
MyThread *thread = new MyThread(this);
thread->create();
thread->run();
// wait till this thread finishes its execution, returns data to main program and main program finishes execution of DoThisWorkAfterThreadReturns()
// after this only execute i++(i.e. next thread)
}
can I wait for every thread to finish before creating a new thread from for loop ? It is necessary to create threads as I am sending backend requests which takes a long time sometimes.
I don't think this can be done simply. Here's one example of a way to do it. I'm using a simple application with a text control and a button. To accomplish what you have described above, I've split the work between 3 functions.
The first function starts the work of the outer loop. In this example, it's an event handler for the button.
The second function spawns a number of threads and basically corresponds to the inner loop described above. The threads in this example are dumb. They simply wait for a random amount of time between 0 and 5 seconds, throw an event to announce they are done, and then delete themselves.
The third function is a thread event handler called when each thread is finished. It basically finishes the work of the outer loop. It checks the number of threads running, and if that is zero it will either start a new iteration of the inner loop or finish.
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/thread.h>
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
class MyThread : public wxThread
{
public:
MyThread(wxEvtHandler *handler,int sleeptime)
: wxThread(wxTHREAD_DETACHED)
{ m_pHandler = handler;m_sleepTime= sleeptime;}
protected:
virtual ExitCode Entry();
wxEvtHandler *m_pHandler;
int m_sleepTime;
};
wxThread::ExitCode MyThread::Entry()
{
// A real application would do something here,
// but for this example the only thing done is sleeping
Sleep(m_sleepTime);
// The work is done. Throw a thread event to announce this to the frame.
wxThreadEvent* exitEvent = new wxThreadEvent();
exitEvent->SetInt(m_sleepTime);
wxQueueEvent(m_pHandler, exitEvent);
return (wxThread::ExitCode)0;
}
class MyFrame : public wxFrame
{
public:
MyFrame( wxWindow* parent, int id = wxID_ANY, wxString title = "Demo",
wxPoint pos = wxDefaultPosition, wxSize size = wxSize(481,466),
int style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
private:
void OnButton(wxCommandEvent& event);
void OnThreadComplete(wxThreadEvent& event);
void SpawnThreads();
int m_outerLoopCounter;
int m_outerLoopLimit;
int m_threadsToUse;
wxCriticalSection m_threadsRunningCS;
int m_threadsRunning;
wxTextCtrl* m_textCtrl;
wxButton* m_button;
};
MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos
, wxSize size, int style )
:wxFrame( parent, id, title, pos, size, style )
{
wxPanel* panel = new wxPanel(this);
wxBoxSizer* szr = new wxBoxSizer( wxVERTICAL );
m_textCtrl = new wxTextCtrl( panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE );
szr->Add( m_textCtrl, 1, wxALL|wxEXPAND, 5 );
m_button = new wxButton( panel, wxID_ANY, "Spawn");
szr->Add( m_button, 0, wxALL, 5 );
panel->SetSizer( szr );
Layout();
srand(time(NULL));
m_outerLoopLimit = 3;
m_threadsToUse = 4;
Bind( wxEVT_THREAD, &MyFrame::OnThreadComplete, this);
m_button->Bind( wxEVT_BUTTON, &MyFrame::OnButton, this );
}
void MyFrame::OnButton(wxCommandEvent& event)
{
m_button->Disable();
m_outerLoopCounter=0;
SpawnThreads();
}
void MyFrame::SpawnThreads()
{
(*m_textCtrl) << "spawning threads for loop " << m_outerLoopCounter+1;
(*m_textCtrl) << " of " << m_outerLoopLimit <<"\n";
m_threadsRunning=0;
for ( int i=0; i<m_threadsToUse; ++i )
{
int sleeptime = rand()%5000;
(*m_textCtrl) << "\tthread " << i << " will sleep for ";
(*m_textCtrl) << sleeptime << " ms.\n";
MyThread* thread = new MyThread(this,sleeptime);
wxCriticalSectionLocker enter(m_threadsRunningCS);
++m_threadsRunning;
if ( thread->Run() != wxTHREAD_NO_ERROR )
{
wxLogError("Can't create the thread!");
delete thread;
--m_threadsRunning;
}
}
}
void MyFrame::OnThreadComplete(wxThreadEvent& event)
{
(*m_textCtrl) << "\tThe thread that slept for ";
(*m_textCtrl) << event.GetInt() << " ms has finished.\n";
// Check the number of threads that are still running
bool canStop = false;
{
wxCriticalSectionLocker enter(m_threadsRunningCS);
--m_threadsRunning;
if ( m_threadsRunning == 0 )
{
canStop=true;
}
}
// If there are zero threads still running, either enter a new iteration
// of the outer loop or stop if the outer loop is complete.
if(canStop)
{
++m_outerLoopCounter;
if ( m_outerLoopCounter<m_outerLoopLimit )
{
SpawnThreads();
}
else
{
(*m_textCtrl) << "All Done.\n";
m_button->Enable();
}
}
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
There are some downsides here. The outer loop counter and the number of threads running need to be tracked with variables available in multiple functions. So they need to be either global variables or members of the frame or app class. Also, I think the variable for the number of running threads should be guarded with a critical section. (I could be wrong, but in the example above, I decided to be safe and used the critical section.)
There may be a simpler way to do this. It's just the first thing I've tried.
wxSemaphore is a counter. wxSemaphore::Wait() waits if the internal counter is zero, otherwise it decrements the counter and returns.
You need the opposite to wxSemaphore, something that waits until the counter is zero.
Set a var (let's call it tVar) that increments when the thread starts and decrements when the thread ends.
class MyThread : public wxThread
{
....
MyThread(someObject *obj, wxEvtHandler *evtH) //someObject is where tVar lives, evtH is where we will post an event
{ m_obj = obj; m_evtH = evtH;}
....
someObject *m_obj;
wxEvtHandler *m_evtH;
};
wxThread::ExitCode MyThread::Entry()
{
//Increment the var
wxCriticalSection locker(m_obj->someCritSec) //someCritSec must be accessible in someObject
locker.Enter();
m_obj->IncrementVar();
locker.Leave() //Allow other threads to increment the var while this thread is still working
//do the task
.....
//and decrement through a message
wxQueueEvent(m_evtH, new wxThreadEvent(wxEVT_COMMAND_MYTHREAD_COMPLETED));
return 0;
}
Class someObject is derived from wxEvtHandler (for example, a wxWindow) so it can receive the message. By an event-table or better by Bind() you have a handler for the thread completion event:
void someObject::OnThreadCompleted(wxThreadEvent&)
{
//decrement the var
--tVar;
//Do something when it reaches 0
if ( tVar == 0 )
DoThisWorkAfterThreadReturns();
}
This solution allows the GUI to be responsive while threads are working. Really no body is waiting. It's that DoThisWorkAfterThreadReturns is only executed when all threads have finished. The logic on which "levels" must "wait" or not is your decision.
There is a small caveat in this solution: if the first created thread finishes before another thread begins running the message will be posted and there's a chance that its handler is called before another thread increments the var.
You can avoid it by having tVar = 1 before any thread is created, and decrementing it (post an event) right after the last thread is created.
I'm using an example from WxWidget wiki to understand how to do the Inter-Thread and Inter-Process communication. I don't know why the example works but my code, very similar, doesn't at line "m_QueueCount.Wait(); // wait for semaphore (=queue count to become positive)"
When I run the code an exeption is showed:
First-chance exception at 0x00B2DB61 in TeamTranslate.exe: 0xC0000005:
Access violation reading location 0x00000078.
If there is a handler for this exception, the program may be safely
continued.
and the breakpoint is at line 1749 of xstring file. This is the code:
size_type length() const _NOEXCEPT
{ // return length of sequence
return (this->_Mysize);
}
I hope someone can tell me why this doesn't work!
Thank you in advance.
header:
class QueueMSG
{
public:
QueueMSG(wxEvtHandler* pParent) : m_pParent(pParent) {}
void AddJob(MessagePTR job); // push a job with given priority class onto the FIFO
MessagePTR Pop();
void Report(const Message::tCOMMANDS& cmd, MessagePTR arg); // report back to parent
size_t Stacksize(); // helper function to return no of pending jobs
private:
wxEvtHandler* m_pParent;
std::vector<MessagePTR> m_Jobs; // multimap to reflect prioritization: values with lower keys come first, newer values with same key are appended
wxMutex m_MutexQueue; // protects queue access
wxSemaphore m_QueueCount; // semaphore count reflects number of queued jobs
};
class WorkerThread : public wxThread
{
public:
WorkerThread(QueueMSG* pQueue) : m_pQueue(pQueue) { wxThread::Create(); }
private:
QueueMSG* m_pQueue;
BingTranslate bng;
virtual wxThread::ExitCode Entry();
virtual void OnJob();
}; // class WorkerThread : public wxThread
cpp:
void QueueMSG::AddJob(MessagePTR job) // push a job with given priority class onto the FIFO
{
wxMutexLocker lock(m_MutexQueue); // lock the queue
m_Jobs.push_back(job); // insert the prioritized entry into the multimap
m_QueueCount.Post(); // new job has arrived: increment semaphore counter
} // void AddJob(const tJOB& job, const tPRIORITY& priority=eNORMAL)
MessagePTR QueueMSG::Pop()
{
std::vector<MessagePTR>::iterator element;
m_QueueCount.Wait(); // wait for semaphore (=queue count to become positive)
m_MutexQueue.Lock(); // lock queue
element = m_Jobs.begin(); // get the first entry from queue (higher priority classes come first)
m_Jobs.erase(m_Jobs.begin()); // erase it
m_MutexQueue.Unlock();// unlock queue
return *element; // return job entry
} // tJOB Pop()
void QueueMSG::Report(const Message::tCOMMANDS& cmd, MessagePTR arg) // report back to parent
{
wxThreadEvent evt(wxEVT_THREAD, wxID_ANY);// cmd); // create command event object
evt.SetPayload<MessagePTR>(arg); // associate string with it
wxQueueEvent(m_pParent, evt.Clone());
//m_pParent->AddPendingEvent(evt); // and add it to parent's event queue
} // void Report(const tJOB::tCOMMANDS& cmd, const wxString& arg=wxEmptyString)
size_t QueueMSG::Stacksize()
{
wxMutexLocker lock(m_MutexQueue); // lock queue until the size has been read
return m_Jobs.size();
}
void WorkerThread::OnJob()
{
MessagePTR job = m_pQueue->Pop(); // pop a job from the queue. this will block the worker thread if queue is empty
bng.translateThis(job);
switch (job->m_cmd)
{
case Message::eID_THREAD_EXIT: // thread should exit
//Sleep(1000); // wait a while
throw Message::eID_THREAD_EXIT; // confirm exit command
case Message::eID_THREAD_JOB: // process a standard job
//Sleep(2000);
m_pQueue->Report(Message::eID_THREAD_JOB, job); // report successful completion
break;
case Message::eID_THREAD_JOBERR: // process a job that terminates with an error
m_pQueue->Report(Message::eID_THREAD_JOB, job);
//Sleep(1000);
throw Message::eID_THREAD_EXIT; // report exit of worker thread
break;
case Message::eID_THREAD_NULL: // dummy command
default: break; // default
} // switch(job.m_cmd)
} // virtual void OnJob()
wxThread::ExitCode WorkerThread::Entry()
{
Message::tCOMMANDS iErr;
m_pQueue->Report(Message::eID_THREAD_STARTED, NULL); // tell main thread that worker thread has successfully started
try {
while (true)
OnJob();
} // this is the main loop: process jobs until a job handler throws
catch (Message::tCOMMANDS& i) {
m_pQueue->Report(iErr = i, NULL);
} // catch return value from error condition
return (wxThread::ExitCode)iErr; // and return exit code
} // virtual wxThread::ExitCode Entry()
Message class:
#pragma once
#include <cstring>
#include <stdio.h>
#include <wx/string.h>
#include <vector>
#include <memory>
enum MSGDirection{
in,
out
};
class Message {
public:
enum tCOMMANDS // list of commands that are currently implemented
{
eID_THREAD_EXIT = wxID_EXIT, // thread should exit or wants to exit
eID_THREAD_NULL = wxID_HIGHEST + 1, // dummy command
eID_THREAD_STARTED, // worker thread has started OK
eID_THREAD_JOB, // process normal job
eID_THREAD_JOBERR // process errorneous job after which thread likes to exit
}; // enum tCOMMANDS
Message(MSGDirection dir, wxString from, wxString message, wxString language_org, wxString language_dest) : m_message(message), m_dir(dir), m_from(from), m_language_orig(language_org), m_language_dest(language_dest), m_cmd(eID_THREAD_EXIT){
time_t rawtime;
struct tm* timeinfo;
char timestamp[100];
time(&rawtime);
timeinfo = localtime(&rawtime);
strftime(timestamp, 100, "%c", timeinfo);
m_timestamp = timestamp;
}
Message() : m_cmd(eID_THREAD_NULL) {}
~Message(){ }
void setIO(MSGDirection dir);
MSGDirection getIO(){ return m_dir; };
void setFrom(char* dir);
wxString getFrom(){ return m_from; };
void setMSG(wxString dir){
m_message = dir;
};
wxString getMSG(){ return m_message; }
wxString getTranslated(){ return m_translated; }
wxString getTimeStamp(){ return m_timestamp; }
wxString getLanguageOrig(){ return m_language_orig; }
wxString getLanguageDest(){ return m_language_dest; }
void setSrtTranslate(wxString str){ m_translated = str; }
tCOMMANDS m_cmd;
private:
MSGDirection m_dir;
wxString m_language_orig;
wxString m_from;
wxString m_message;
wxString m_timestamp;
wxString m_language_dest;
wxString m_translated;
};
typedef std::shared_ptr<Message> MessagePTR;
typedef std::shared_ptr<std::vector<MessagePTR>> MessageQueuePTR;
In your QueueMSG::Pop() call, you use an iterator even after it has been invalidated. Even if it wasn't invalidated by your call to erase (which it will be, see here), you cannot guarantee its validation after you have unlocked the mutex since you can be time-sliced at that point and then another thread can modify the container. Therefore you should instead do:
MessagePtr msg;
m_QueueCount.Wait();
{
wxMutexLocker lock( m_MutexQueue );
auto element = m_Jobs.begin();
msg = *element;
m_Jobs.erase(element);
}
return msg;
In their code - only slightly different than yours - they dereference the iterator to get a pair and return the contained second element via this line:
element=(m_Jobs.begin())->second; // that '->'
which is why they would not suffer from undefined behaviour.
I am currently calling some methods from an external lib file. Is there a way for these methods to callback functions in my application once they are done as these methods might be running in separate threads?
The following diagram shows what I am trying to achieve
I wanted to know what is the best way of sending a message back to the calling application ? Any boost components that might help ?
Update after the edit:
It's not clear what you have. Do you control the thread entry point for the thread started by the external library (this would surprise me)?
Assuming:
the library function accepts a callback
assuming you don't control the source for the library function, not the thread function started by this library function in a background thread
you want to have the callback processed on the original thread
you could have the callback store an record in some kind of queue that you regularly check from the main thread (no busy loops, of course). Use a lock-free queue, or synchronize access to the queue using e.g. a std::mutex.
Update Here's such a queuing version Live on Coliru as well:
#include <thread>
#include <vector>
//////////////////////////////////////////////////////////
// fake external library taking a callback
extern void library_function(int, void(*cb)(int,int));
//////////////////////////////////////////////////////////
// our client code
#include <iostream>
#include <mutex>
void callback_handler(int i, int input)
{
static std::mutex mx;
std::lock_guard<std::mutex> lk(mx);
std::cout << "Callback #" << i << " from task for input " << input << "\n";
}
//////////////////////////////////////////////////////////
// callback queue
#include <deque>
#include <future>
namespace {
using pending_callback = std::packaged_task<void()>;
std::deque<pending_callback> callbacks;
std::mutex callback_mutex;
int process_pending_callbacks() {
std::lock_guard<std::mutex> lk(callback_mutex);
int processed = 0;
while (!callbacks.empty()) {
callbacks.front()();
++processed;
callbacks.pop_front();
}
return processed;
}
void enqueue(pending_callback cb) {
std::lock_guard<std::mutex> lk(callback_mutex);
callbacks.push_back(std::move(cb));
}
}
// this wrapper to "fake" a callback (instead queuing the real
// callback_handler)
void queue_callback(int i, int input)
{
enqueue(pending_callback(std::bind(callback_handler, i, input)));
}
int main()
{
// do something with delayed processing:
library_function(3, queue_callback);
library_function(5, queue_callback);
// wait for completion, periodically checking for pending callbacks
for (
int still_pending = 3 + 5;
still_pending > 0;
std::this_thread::sleep_for(std::chrono::milliseconds(10))) // no busy wait
{
still_pending -= process_pending_callbacks();
}
}
//////////////////////////////////////////////////////////
// somewhere, in another library:
void library_function(int some_input, void(*cb)(int,int))
{
std::thread([=] {
for (int i = 1; i <= some_input; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 5000)); // TODO abolish rand()
cb(i, some_input);
}
}).detach();
}
Typical output:
Callback #1 from task for input 5
Callback #2 from task for input 5
Callback #1 from task for input 3
Callback #3 from task for input 5
Callback #2 from task for input 3
Callback #4 from task for input 5
Callback #5 from task for input 5
Callback #3 from task for input 3
Note that
output is interspersed for both worker threads
but because the callbacks queue is FIFO, the sequence of callbacks per worker thread is preserved
This is what I thought of, before you edited the question: Live on Coliru
#include <thread>
#include <vector>
extern int library_function(bool);
static std::vector<std::thread> workers; // TODO implement a proper pool
void await_workers()
{
for(auto& th: workers)
if (th.joinable()) th.join();
}
template <typename F, typename C>
void do_with_continuation(F f, C continuation)
{
workers.emplace_back([=] () mutable {
auto result = f();
continuation(result);
});
}
#include <iostream>
#include <mutex>
void callback(int result)
{
static std::mutex mx;
std::lock_guard<std::mutex> lk(mx);
std::cout << "Resulting value from callback " << result << "\n";
}
int main()
{
// do something with delayed processing:
do_with_continuation(std::bind(library_function, false), callback);
do_with_continuation(std::bind(library_function, true), callback);
await_workers();
}
// somewhere, in another library:
#include <chrono>
int library_function(bool some_input)
{
std::this_thread::sleep_for(std::chrono::seconds(some_input? 6 : 3));
return some_input ? 42 : 0;
}
It will always print the output in the order:
Resulting value from callback 0
Resulting value from callback 42
Notes:
make sure you synchronize access to shared state from within such a callback (in this case, std::cout is protected by a lock)
you'd want to make a thread pool, instead of an ever-growing vector of (used) threads
If there is something more difficult than debugging a multithreaded app that is trying to describe the bug itself.
I have two boost::threads (application and display).
Both use the same asio::io_service to do their work.
The display thread has a std::set of type window* which is a class I use to wrap winapi window management.
I use a custom message queue to communicate these two threads.
One of these messages (terminate) is used to notify the display thread that it must not "post" any more methods and that it must call thread_group.remove_thread and remove itself.
The thread has a variable (state) that flags the state of the thread (running, paused, terminated).
If it is running it "posts" it's update() method that iterates an std::set and calls the update method in each window* it contains.
If it is terminated, it clears the std::set, removes itself from the thread_group and doesn't post any more work.
The problem: Once a while, when trying to close the app, the thread's update method gets ran after the thread got "terminated" and the std::set got cleared. Then the update method tries to iterate the std::set and a SIGSEGV takes place. This only happens 1 every 10 runs of the application and I'm having a hard time trying to guess what's wrong.
I'll try to post the relevant code, if more is needed I'll try to add it.
int main(int argc, char **argv)
{
boost::asio::io_service ios;
boost::asio::strand strand(ios);
boost::thread_group threads;
owl::system::pump pump;
application app(&threads, &strand, &pump);
owl::system::display display(&strand, &pump);
ios.run();
threads.join_all();
return 0;
}
...
void display::on_terminate()
{
close_all_windows();
}
...
void display::close_all_windows()
{
windows.move_first();
while (!windows.eof())
{
window* win = windows.value();
win->destroy();
delete win;
windows.move_next();
}
windows.clear();
check_no_window();
}
...
void display::on_update()
{
if (windows.size())
{
windows.move_first();
while (!windows.eof())
{
windows.value()->update();
windows.move_next(); // Here happens the SIGSEGV
}
}
}
The class display inherits the class subsystem that manages thread execution. This is the relevant code involving the execution of on_update()
void subsystem::do_update()
{
message* msg;
size_t message_count = messages.size();
for (size_t i=0; i<message_count; i++)
{
msg = messages[i];
process_message(msg);
strand->dispatch(strand->wrap(boost::bind(&message::deallocate, msg)));
}
switch (state)
{
case running:
{
on_update();
}
break;
case paused:
{
// Do not update. Just check the queue and sleep
sleep(10);
}
break;
case terminated:
{
do_terminate();
return;
}
break;
}
strand->post(strand->wrap(boost::bind(&subsystem::check_for_messages, this)));
}
void subsystem::check_for_messages()
{
messages.clear();
pump->get_messages(this, messages);
ios->post(boost::bind(&subsystem::do_update, this));
}
The SIGSEGV occurs exactly when trying to increment the std::set iterator.
Child process PID: 2272
Program received signal SIGSEGV, Segmentation fault.
In std::_Rb_tree_increment(std::_Rb_tree_node_base const*) ()
stl_tree.h:269