Page 2 of 2

Re: wxThread / newbie

Posted: Fri Feb 14, 2020 2:13 am
by art-ganseforth
doublemax wrote:
Fri Feb 14, 2020 1:33 am

Code: Select all

new clsParserThread(Parser,Code))->Run();
in between. That crashes
The Create() is missing (unless you put that into the thread ctor). The whole creation and starting of a thread is a 3-step process.

Code: Select all

clsParserThread *thread = new clsParserThread(Parser,Code);
if( thread->Create() == wxTHREAD_NO_ERROR )
Okay, i will look for "Create()" i the example...

Re: wxThread / newbie

Posted: Fri Feb 14, 2020 8:13 am
by Kvaz1r
art-ganseforth wrote:
Fri Feb 14, 2020 2:13 am
This structure seems to be simelar to the multithreading-structure (one main-script calling several independent sub-scripts to be started at one 'entry-point'). The question is, how to implement it...
If several of your tasks will be appear after running of first task - you need worker pool. Unfortunately there isn't worker pool in wxWidgets though corresponding ticket are open for years - wxThreadPool class for multi-threaded job management.

If there are not many tasks you can run each of them in own thread or use parallel for (once again wxWidgets don't provide this feature but there are many libraries and of course parallel execution algorithms from C++17)

Re: wxThread / newbie

Posted: Fri Feb 14, 2020 11:27 pm
by art-ganseforth

I think i have what i want. I wrote a very small class derived from wxThreadHelper.

Here is the code:

Code: Select all

// Test-application to see id and how two 
// wxThreadHelper-instances work parallel
#define wxUSE_THREADS 1

#include <wx/wx.h>

// --- Standard class-declarations --------------------

class clsApp : public wxApp {
        virtual bool OnInit();

class clsFrame : public wxFrame {
        void    OnClose(wxEvent &ev);
        wxTextCtrl *textCtrl;

// --- Globals ----------------------------------------

wxTimer    *timer   = NULL;
clsFrame   *mainWin = NULL;
int         idCT    = 0;

// The thread-class (declaration and implementation)

class clsThread : public wxThreadHelper, public wxEvtHandler {
        virtual void DoJob           ();                // Initiate a job without timer
        virtual void OnDoJob         (wxEvent &ev);     // Initiate jobs the global timer
        virtual void OnInternalTimer (wxEvent &ev);     // This timer is needed, to execute jobs parallel. 
        virtual wxThread::ExitCode Entry ();
        wxTimer pTimer;
        int ct  =  0;
        int id  = -1;

clsThread :: clsThread() : wxThreadHelper(), wxEvtHandler() { 
    pTimer.Connect    (wxEVT_TIMER, wxEventHandler(clsThread::OnInternalTimer), NULL, this);
    if (CreateThread() == wxTHREAD_NO_ERROR )   GetThread()->Run();     else wxMessageBox("error");

wxThread::ExitCode clsThread::Entry() {         id = idCT++;
    // As an example, i connect both threads to the timer, but the second one with some delay.
    // Also the first thread ist disconnected after 10 secondas.
    if ( id)    Sleep(2000);    timer->Connect    (wxEVT_TIMER, wxEventHandler(clsThread::OnDoJob), NULL, this);
    if (!id) {  Sleep(10000);   timer->Disconnect (wxEVT_TIMER, wxEventHandler(clsThread::OnDoJob), NULL, this);    }
    while(1)    Sleep(10000);                           // Endless loop - the 'job' is done in the OnTimer-function below

    // --- Old version with TestDestroy() ---
    //while (!GetThread()->TestDestroy())         Sleep(100);         // Endless loop - the 'job' is done in the OnTimer-function below
    //return NULL;

void clsThread :: OnDoJob(wxEvent &ev) {        ev.Skip();      DoJob();    };

void clsThread :: DoJob() { 
    pTimer.Start (0, wxTIMER_ONE_SHOT);                 // By using a second timer, this function returns immideately.  

void clsThread :: OnInternalTimer(wxEvent &ev) {   
    // Do the job here. I used a simple id to decide what to do, but you probably may derive 
    // classes with different OnInternalTimer-functions.  
    if ( id) {  Sleep(300);     mainWin->textCtrl->AppendText(wxString::Format("Thread-id: %d - Jobs done: %d\n", id, ++ct));     }
    else     {                  mainWin->textCtrl->AppendText(wxString::Format("Thread-id: %d - Jobs done: %d\n", id, ++ct));     }

// End of the thread-class. Nothing more is needed

// --- Standard class-implementations -----------------

bool clsApp::OnInit() {

    if (!wxApp::OnInit())                       return false;

    timer = new wxTimer();                      timer->Start(1000);
    SetTopWindow(new clsFrame());

    // For demonstration, on sub-thread is started here and a jom is initiated. 
    // The other in clsFrame:: clsFrame() below.
    (new clsThread())->DoJob();           

    return true;

clsFrame:: clsFrame() : wxFrame(NULL, -1, "", wxPoint(250, 150), wxSize(500, 300) ) {
    mainWin  = this;
    textCtrl = new wxTextCtrl(this, -1, "", wxPoint(0,0), wxDefaultSize, wxTE_MULTILINE);

    Connect (wxEVT_CLOSE_WINDOW, wxEventHandler(clsFrame::OnClose), NULL, this);

     // The first sub-thread is started here. The other one in clsApp::OnInit() above.
    clsThread *tmp = new clsThread();
    // Initiate a job for this thread. Both threads have an output, even if they are started 
    // almost at the same time.   

    // ...but there is no cue inside of the thread. Therefore this one overwritses the line 
    // before. Otherwise the job-counter would be incremented by two. 
    // However: Calling a thread twice, does not crash the program.

void clsFrame:: OnClose(wxEvent &ev) {          delete timer;       ev.Skip();      }

I've not not yet, if the threads are really distributed to different CPU-cores, but i hope so (i don't see any reason, why not).


I will implement this in my project and (if i don't forget it) i'll report the result here...

Re: wxThread / newbie

Posted: Fri Feb 14, 2020 11:56 pm
by doublemax
Sorry, this will not work as you expect, event handlers (in this case your timer event handlers) are always called in the context of the main thread.

Re: wxThread / newbie

Posted: Sat Feb 15, 2020 12:51 am
by art-ganseforth
On my system, the posted code worked as it is. But for my application i don't even need to initiate the threads by a global timer.

I've already a version that doesn't crash my application (but i have to implement a sync-technic). This new version is a fire-and-forget one. I call it with with a char* that refers the code to be executed, and with a pointer to the interpreter-instance, that should interpret it (a subinstance of the calling one). After execution the Interpreter returns to the calling clsTask-instance, so it can delete itself by calling delete this.

And the best: All CPU-cores are in use...