Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

QEventLoop instance for my "worker" to do work... - correct idea?



  • Hey

    Lately I have builded a worker with 2 mutexes, 2x QVector<std::function<void>> jobs and a while loop to run over my vec of functions and execute them 1 by 1. I have 2 of the vecs because I have 2 types of priorities, 1 that is on a while loop every 200ms and 1 that is every 50ms. So this way I have some kind of "Priority" going on.

    As much as I like how it works, I have to deal with around 2 mutexes, and a lot more stuff which I'm fairly sure is a bad "design" as its my 1st design so its bad.

    In any case I was recently thinking if I could use Qt existing system to my advantage.

    Now I know that main app has QEventLoop that runs eerything and I can send functions to it. Either via signal/slot or QMetaObject::invokeMethod().
    What I do wonder is, if I can start my own loop + send functions/lambdas to my own "worker-loop"...

    • side note is that I've read that there are "priority" options so I could send lambda function with different priority and then hope that the Qt loop will execute them in accordance to priority and then submission order...

    I was messing a little in pySide as my test bed... didn't get too far since I didnt use invokeMethod there and passing lambda the way I did does notw ork... but can any1 have a look and let me know if my understanding is correct?

    def runMyTest() would send job to the given Worker-loop object to perform work

    I would send stuff like

    auto f = [=](){
    do stuff}
    QMetaObject::invokeMethod(worker,f,Qt::HighEventPriority); - I now learned that this is invalid... and I need to post QEvent with priority... 
    
    or 
    
    auto f = [&](){
    run otherStuff
    }
    QMetaObject::invokeMethod(worker,f)
    

    Example code:

    from PySide2.QtCore import *
    from PySide2.QtWidgets import *
    from PySide2.QtGui import *
    import sys
    
    print("Hello event loop")
    
    
    class myWorker(QObject):
        def __init__(self):
            super().__init__()
            self.eventL = QEventLoop()
    
        def startEvent(self):
            print("Starting loop - this will 'keep' the thread busy for as long as loop runs?")
            self.eventL.exec_()
    
    
    def runMyTest(worker):
        print("Sending command?")
        # QMetaObject.invokeMethod(worker, lambda val: (print("I run in myWorker event loop now ? ")))
        ### Does QMetaObject invokeMethod offer priority ?
        # QMetaObject::invokeMethod(worker,[&](){some code that I want to run in the event loop of the myWorker class})
    
    
    app = QApplication(sys.argv)
    
    w = QWidget()
    w.show()
    
    lay = QGridLayout()
    w.setLayout(lay)
    btn = QPushButton("Click Me")
    lay.addWidget(btn)
    
    worker = QThread()
    workerItem = myWorker()
    workerItem.moveToThread(worker)
    worker.started.connect(workerItem.startEvent)
    worker.start()
    
    btn.clicked.connect(lambda x: (print(x), runMyTest(workerItem)))
    print("Setup ended")
    sys.exit(app.exec_())
    
    

    Ok in regards to passing lambda in invokeFunction. I now think I need to create my own Event and pass my own Event with lambda inside it as member to the QEventLoop of the worker... > https://ryanclouser.com/2015/07/16/Qt-Passing-Custom-QEvent-Data/ as thats the only way to control the priority of the action....

    Am I going in the right dirrection here?

    TIA.


  • Lifetime Qt Champion

    @Dariusz It's not clear what you actually want to do.
    If you just want to execute some functions in other threads then you can simply use https://doc.qt.io/qt-5/qtconcurrentrun.html


  • Lifetime Qt Champion

    @Dariusz said in QEventLoop instance for my "worker" to do work... - correct idea?:

    What I do wonder is, if I can start my own loop + send functions/lambdas to my own "worker-loop"...

    Why? QThread::run() starts an event loop and you can send data via signal/slots to objects living in this threads - so what would be the advantage to create another eventloop and break the QThreads own one?



  • @jsulm said in QEventLoop instance for my "worker" to do work... - correct idea?:

    @Dariusz It's not clear what you actually want to do.
    If you just want to execute some functions in other threads then you can simply use https://doc.qt.io/qt-5/qtconcurrentrun.html

    I need 1 thread that is always alive to run all the time. I need to have a serialized - priority-based execution of my functions to another library. - cant have QtConcurrent as that could spawn multiple threads and crash other library as its not thread save.

    @Christian-Ehrlicher said in QEventLoop instance for my "worker" to do work... - correct idea?:

    @Dariusz said in QEventLoop instance for my "worker" to do work... - correct idea?:

    What I do wonder is, if I can start my own loop + send functions/lambdas to my own "worker-loop"...

    Why? QThread::run() starts an event loop and you can send data via signal/slots to objects living in this threads - so what would be the advantage to create another eventloop and break the QThreads own one?

    As far as I can tell, if the object inside QThread runs its function - like start function, then the thread dies at the end of the function? I though I always have to put a "while" loop inside that object to keep thread alive... hmhmhm ?

    Ps wow that was fast reply, thanks boys!


  • Lifetime Qt Champion

    @Dariusz said in QEventLoop instance for my "worker" to do work... - correct idea?:

    like start function, then the thread dies at the end of the function?

    Not if it has its own event loop



  • @jsulm said in QEventLoop instance for my "worker" to do work... - correct idea?:

    @Dariusz said in QEventLoop instance for my "worker" to do work... - correct idea?:

    like start function, then the thread dies at the end of the function?

    Not if it has its own event loop

    Is that not what I did with myWorker then? I'm bit lost how to set it up then :- (


  • Lifetime Qt Champion

    @Dariusz With QThread::run() you already get an event loop, there is no need to create one manually.
    See https://doc.qt.io/qt-5/qthread.html#run
    "The default implementation simply calls exec()"

    https://doc.qt.io/qt-5/qthread.html#exec
    Enters the event loop



  • @jsulm said in QEventLoop instance for my "worker" to do work... - correct idea?:

    @Dariusz With QThread::run() you already get an event loop there is no need to create one manually.
    See https://doc.qt.io/qt-5/qthread.html#run
    "The default implementation simply calls exec()"

    https://doc.qt.io/qt-5/qthread.html#exec
    Enters the event loop

    Hmm

    So, in this case, I don't create myWorker(QObject) at all. I create myThreadWorker(QThread), reimplement def run() function and call exec() in that.

    Then if I want to send function to it I can either do :
    QMetaObject::invokeMethod(myThreadWorker,={}) // giving myThreadWorker as parent, should mean that the thread will execute it?
    or
    send myQEvent() subclass of QEvent to it.
    I suppose I need to override def event() on myThreadWorker to capture events it receives and then handle my events properly?

    TIA


  • Lifetime Qt Champion

    @Dariusz Take a look at documentation https://doc.qt.io/qt-5/qthread.html
    There is an example how to use worker objects.

    Do you really need events to communicate with the thread? The easiest way is actually to use signals/slots.



  • @jsulm said in QEventLoop instance for my "worker" to do work... - correct idea?:

    @Dariusz Take a look at documentation https://doc.qt.io/qt-5/qthread.html
    There is an example how to use worker objects.

    I went over the QThread docs - briefly... (I've read it lots of time in past bust just to refresh...)

    1st examples shows the workflow with moving object to thread which is what I did above in pySide2.
    The second example talks about reimplementing run() functions.

    In both of these examples as far as I can tell, the thread will die at the end of given functions. I cant let it die, I want it to be available and provide work connection with another library for as long as I want.

    So I'm lost again as to how I can use it.

    Do you really need events to communicate with the thread? The easiest way is actually to use signals/slots.

    I need to be able to send jobs to that thread with different priority of execution, so some jobs might need to happen before other ones.


    Edit from what I can read
    "Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread."

    This tells me that I do in fact need myWorker(QObject) class and the moveToThread action to have my slots/signals/ executed in other thread, as run() does not allow for it to happen.


  • Lifetime Qt Champion

    @Dariusz said in QEventLoop instance for my "worker" to do work... - correct idea?:

    I cant let it die

    Then call exec() in run() - it will start the event loop.

    "I need to be able to send jobs to that thread with different priority of execution, so some jobs might need to happen before other ones" - I still don't see a need for events. Send the jobs as signals. Priority handling is something you need to implement.



  • @jsulm said in QEventLoop instance for my "worker" to do work... - correct idea?:

    @Dariusz said in QEventLoop instance for my "worker" to do work... - correct idea?:

    I cant let it die

    Then call exec() in run() - it will start the event loop.

    "I need to be able to send jobs to that thread with different priority of execution, so some jobs might need to happen before other ones" - I still don't see a need for events. Send the jobs as signals. Priority handling is something you need to implement.

    Hmmm yeah the exec() would keep it alive allowing me to send stuff to that thread. But it would not be processed by that thread according to this documentation :

    It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots and invoked methods will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.

    Priority handling is something you need to implement.

    Well yes, and no. QEventLoop and now that I can read QEvents have priority options. So if I create event and send it to that thread, that event will be executed in accordance to the priority of it. So Qt offers priority control as well. I just have to use QEvents when sending jobs to Thread... Not sure how to send event to thread yet... but event would be something like:

    class myEvent : public QEvent(){
    /// specify type to MaxUser+xx
    public:
        myEvent()
        ~myEvent()
        std::function<void> mFunction;
    }
    

    Then I can send that to either myThreadWorker(QThread) or myWorker(QObject). Not sure which yet as I need to read a bit more...


    As far as I can tell this is somewhat the workflow > https://stackoverflow.com/questions/6208339/qt-correct-way-to-post-events-to-a-qthread
    https://doc.qt.io/qt-5/qcoreapplication.html#postEvent


  • Lifetime Qt Champion

    @Dariusz Well, of course you should not send signals to QThread instance as it is not the thread itself, it is just an object which manages the thread. Instead you send signals to objects living in that thread (use moveToThread to move objects to the thread).



  • @jsulm said in QEventLoop instance for my "worker" to do work... - correct idea?:

    @Dariusz Well, of course you should not send signals to QThread instance as it is not the thread itself, it is just an object which manages the thread. Instead you send signals to objects living in that thread (use moveToThread to move objects to the thread).

    Yeah, which again begs the question. how do I start the thread and object without object closing down the thread. So we use 1st solution from docs. But that will end when function finish.... So how do I start the thread with object and not let it finish but still run event loop to process events... ?


  • Lifetime Qt Champion

    @Dariusz said in QEventLoop instance for my "worker" to do work... - correct idea?:

    So how do I start the thread with object and not let it finish but still run event loop to process events... ?

    By simply starting the QThread with QThread ::start() and not overwriting QThread::run()



  • @Christian-Ehrlicher The thread don't get autodeleted once it runned its empty function? o.O I need to test it! :D


Log in to reply