Simple multithreading question

Hi all,

So I am working on a GUI application that loads, processes, and writes MIDI files. Everything works, but processing can take about 5 seconds for large files (there is a lot of math so this is understandable) and hitting the “Process” button locks up the GUI while that code is running, and I am supposing that this is because I haven’t done any multithreading.

What is the best approach to take? I am thinking that I will create a friend Thread class of the MainComponent that executes the buttonClicked() code for the “Process” button. So would my MainComponent need a timer that periodically checks to see if “Process” has been completed ? Should I initialize a thread on start-up and have it sleep when not in use?

Thanks in advance,

1 Like

Make your background thread inherit from ChangeBroadcaster.
Make your GUI inherit from ChangeListener. Make it listen to your background thread.
When the background thread finishes processing, call sendChangeMessage(); and it’ll notify your GUI that it has finished processing asynchronously.

struct T : Thread, ChangeBroadcaster
{
     T() : Thread("Background Thread") { startThread(); } 
    ~T() { stopThread(300); }
     void run() override 
    { 
        //do a bunch of work, then at the end before the thread exits:
        sendChangeMessage(); 
    } 
};

struct GUI : Component, ChangeListener
{
    GUI() { t.addChangeListener(this); }
    ~GUI() { t.removeChangeListener(this); } 
    void changeListenerCallback(ChangeBroadcaster* cb) override
    {
        if( auto* t = dynamic_cast<T*>(cb) )
        {
              //your thread finished processing.
        }
    }

    T backgroundThread;
};

3 Likes

Hey thanks a ton, this looks like what I need. To be clear, what is the purpose of the dynamic_cast call?

You might be listening to other ChangeBroadcasters. this is how you know it was the background thread that sent the change message.

1 Like

Naming the parameter to the changeListenerCallback as in the docs might have made this more obvious. It’s not at all necessary though, you can just compare the source pointer to the address of backgroundThread.

    void changeListenerCallback(ChangeBroadcaster* source) override
    {
        if(source == &backgroundThread)
        {
            // your thread finished processing.
        }
    }

This is a bit more readable IMO.

2 Likes

Yes that does look a bit cleaner, thank you for that

So for those viewing this thread in the future wondering about the dynamic_cast call referenced by @matkatmusic 's example code and/or having trouble with the direct comparison between the changeListenerCallback() argument and background thread referenced by @asimilon 's example code, I’ve done a bit of digging in and this is what I found:

The direct comparison in @asimilon 's code only works if the ChangeBroadcaster that has called sendChangeMessage() is a ChangeBroadcaster object itself and not a different class that has inherited ChangeBroadcaster.

So say you are like me and have created a ProcessorThread class that inherits Thread and ChangeBroadcaster to signal when the Thread run() method has completed, you must cast your ProcessorThread instance to a ChangeBroadcaster in order to compare it to the ChangeBroadcaster* that is the argument of changeListenerCallback() and that is the purpose of dynamic_cast.

Edit: and remember that inheritance is private by default, so whatever class is inheriting ChangeBroadcaster must inherit it publicly, i.e. its definition will look like this:

class ProcessorThread : public Thread, public ChangeBroadcaster {

//…

}

I wasn’t sure if that’s true, so I made this testcase, and it is not true.

you can do either approach. dynamic_cast<> or compare the address directly.

Also, if you use struct instead of class, everything is public by default and you have to type a little bit less!

2 Likes

The only reason for that comparison to fail is a private or ambiguous base. Also, when you know for certain the subtype of the object pointed to, you can just static_cast. You need dynamic_cast when the subtype is unknown, or in diamond schemes.

4 Likes

Oh yeah looks like you’re both right. Thanks @matkatmusic and @kamedin this was very informative.