Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multi-threading design along with an undo/redo stack

I have this call stack to perform a heavy computation:

// QML

StyledButton {
    onButtonClicked: {
        registeredCppClass.undoHandler.createCommand()
    }
}
void UndoHandler::createCommand()
{
    m_undoStack->push(new Command());
}
class Command : public QUndoCommand
{
public:
    Command();
    virtual ~Command();

    virtual void undo();
    virtual void redo();
    
    // ...
private:
    // Handler does the logic
    LogicHandler *m_logicHandler;
    // Output by logic handler
    QString m_outputName;
};
void Command::redo()
{
    
    if (/* */) {
        
    } else {
        // Run heavy computation
        m_outputName = m_logicHandler->run();
    }
}
QString LogicHandler::run()
{
    // Heavy computation starts
}

Intention

I intend to implement QThread by this approach to prevent GUI from getting non-responsive while heavy computation is being done. But I don't know where QThread and Worker class need to be implemented. Should they be within:

  • UndoHandler::createCommand
  • Command::redo
  • LogicHandler::run
  • ... ?

What is the best location for QThread and Worker considering their signal-slot connections?

like image 429
user3405291 Avatar asked Dec 20 '25 12:12

user3405291


1 Answers

The general advice is to never read QThread documentation. Follow that up with never read Linux thread documentation. I say this as someone who has written quite a few books on software development.

The long answer is threading wasn't well thought out early on and there is a lot of, now bad, information out there. During Qt 3.x and I believe early Qt 4.x one was supposed to derive a class from QThread and override the run(). You can imagine just how well that worked for newbie developers unfamiliar with threads in general and unable to design in mutex or other access protection when manipulating things in multiple threads.

Your design makes it appear you have read some of this documentation. It's still floating around out there.

At some point during Qt 4.x we were no longer supposed to derive from QThread. Instead we were supposed to just create a QThread and moveToThread(). Kinda sorta worked but you could still end up with "dangling threads" if your program didn't follow the happy path through the code.

Around the same time, at least as far as my exposure, we also got a global thread pool.

Your design is really flawed because you looked at old doc. Not your fault. The old doc tends to turn up first in searches.

Visit this GitHub repo and pull down the project. The only dev_doc setup documentation I have completed is for Fedora. I will be working on Ubuntu this morning if I don't get interrupted. Be sure to check out the diamond-themes branch.

Yes, this is using CopperSpice, but CopperSpice is a fork of Qt 4.8 and this is the only concrete code example I could think of off the top of my head. You can build and run the editor or you can poke-and-hope by reading advfind_busy.cpp. What you are looking for is how QFuture is used. That source file is only about 200 lines long and it has a short header file.

Throw out your current design. You need QFuture and QtConcurrent::run().

Note: The header files for these things are different in name and location when compared to current Qt 5.x. That much you will need to look up if you choose to stay with Qt. How you use this stuff is not.

Note 2: If you don't have some kind of throttle control to limit each of these tasks to a single thread instance you will need to dynamically create and destroy QFuture objects. This means you have to have some kind of list or vector keeping track of them and your object destructor needs to walk that list killing off the threads and deleting the objects.

If you want to go on a journey setting up CopperSpice on Ubuntu it is spread across a multi-part blog post starting here.

like image 154
user3450148 Avatar answered Dec 22 '25 01:12

user3450148



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!