Conceptual doubts about Implementations of QThreads



  • Hi everyone!
    I must be one of the many QT users that still struggles with the QThread documentation.
    I know there are two main approaches, reimplementing the run method (The one I almost always use, and I know that the community isnt very fond on it) and creating a Worker, and moving it to a QThread.

    Due to the nature of my program, I now need to use the worker approach, and theres some points I dont completelty understand.

    When I think of a thread, i think of a segment of code thats running all the time. For example, when I reimplement my run method, I do something like

    //Variables that are in my Thread Instance but are accesible by my main program
    QMutex Mutex;
    QSemaphore Semaphore;
    QQueue<QByteArray> QueueArray;
    
    
    void MyClass::run(void)
    {
         //Here any initalizations, as they cant go on the constructor if i want them to live in this thread
         while(1)
         {
              Semaphore.acquire();//Semaphore shared with MainProgram
              Mutex.lock();//Mutex to protect my queue with info
              QByteArray Buffer=QueueArray.dequeue();//I retrieve a package i want to process
              Mutex.unlock();//I protect again my list
    
              //Here I do all time consuming procceses
         }
    }
    

    Here its easy to see that there will be only 1 Thread, working all the time, and if theres no more info to process, It would just enter in an "idle"state (Because of the Semaphore)

    Now, lets assume I wanted to do something similar with the worker approach... Should I create one NEW worker for each QByteArray I want to procces? And then, when the thread finishes, is everything destroyed?

    I tried some tests with some example codes, for example this one

    QThread* thread = new QThread;
    Worker* worker = new Worker();
    worker->moveToThread(thread);
    connect(worker, SIGNAL (error(QString)), this, SLOT (errorString(QString)));
    connect(thread, SIGNAL (started()), worker, SLOT (process()));
    connect(worker, SIGNAL (finished()), thread, SLOT (quit()));
    connect(worker, SIGNAL (finished()), worker, SLOT (deleteLater()));
    connect(thread, SIGNAL (finished()), thread, SLOT (deleteLater()));
    thread->start();
    

    This works like a charm the first time, but if I want to start again the thread, nothing happens.
    I suppose that the deleteLater() method must clean all of the resources, delete the worker and maybe the thread itself.
    So my question... Am i supposed to create the Worker, and make this 5 connections EVERY TIME i want to process some data?
    I Had the idea (Maybe im wrong), that creating threads was time consuming, so thats why a single thread that pasues itelsf sounds better that creating it each time.
    Because of the nature of my program, I would need to launch threads at a rate of 2 per second (approx).
    Program runs on a server with 8 cores, so the idea is to have only 8 threads.
    I think its more appropiate to use the woker approach, because I have more control when each of the oparations finishes, and it allowes my main program, to schedule properly the information between my 8 consuming threads.

    Any tip or suggestion?
    Thanks in advance


  • Moderators

    @NicolasKsi So basically you create a single thread for your worker. That thread then gets it's own event loop. When you signal the thread the signal is processed on an event loop running on the worker thread. It does whatever the task is and emits a signal. You catch that signal on your main thread (assuming main here).

    So just like the style you are used to, the thread is always running but is sometimes idle. The event loop handles all of that.

    You could of course do fire and forget type threads where it does the work and then when done exits and cleans up but that would be kind of annoying in my opinion to work with. It would require a lot more code.

    Quick example, let's say you need a thread to add 2 numbers (trivial I know ;)), you would do something like this:

    class Adder : public QObject
    {
       Q_OBJECT
    public slots:
       void add(int a, int b)
       {
          emit doneAdding(a+b);
       }
    
    signals:
       void doneAdding(int result);
    };
    
    // ... in your implementation
    auto thread = new QThread();
    auto adder = new Adder();
    adder->moveToThread(thread);
    connect(adder, SIGNAL(doneAdding(int)), this, SLOT(addResult(int)));
    connect(this, SIGNAL(myAdd(int, int)), adder, SLOT(add(int, int)));
    thread->start();
    
    // ... later on
    // you can emit your add signal however you want.. link it to a push button, emit it in code, etc.
    
    // push button example:
    connect(myButton, SIGNAL(clicked()), adder, SLOT(add(int, int)));
    
    // in code
    emit myAdd(1, 1);
    

    Hope that helps make it a bit clearer and shows how easy the "preferred" Qt threading method is. :)



  • @NicolasKsi @ambershark
    if it is fire and forget type threads you want to do, than QtConcurrent is probably the way to go. It makes calling a single function in a different Thread much easier than the worker or reimplement approach.

    For everything more complex like a service that listens on a port or waits for external signal before something happens, the worker approach is of course better,



  • This post is deleted!


  • This post is deleted!


  • @ambershark

    Ok, I've been running some tests with your code. I got it to run, but im not sure the best way of how to do it.

    I have a simple question. On my program I have 8 Threads, and 8 Workers.
    So, to instance them I would do something like this (Following your example with the Adder class)

    QThread *thread[8];
    Adder *adder[8];
    for(int i=0;i<8;i++)
    {
         thread[i]=new QThread;
         adder[i]=new Adder;
         adder[i]->moveToThread(thread[i]);
         connect(adder[i],SIGNAL(doneAdding(int)),this,SLOT(addResult(int)));
         thread[i]->start();
    }
    connect(this,SIGNAL(myAdd0(int,int)),adder[0],SLOT(add int, int)));
    connect(this,SIGNAL(myAdd1(int,int)),adder[1],SLOT(add int, int)));
    connect(this,SIGNAL(myAdd2(int,int)),adder[2],SLOT(add int, int)));
    connect(this,SIGNAL(myAdd3(int,int)),adder[3],SLOT(add int, int)));
    connect(this,SIGNAL(myAdd4(int,int)),adder[4],SLOT(add int, int)));
    connect(this,SIGNAL(myAdd5(int,int)),adder[5],SLOT(add int, int)));
    connect(this,SIGNAL(myAdd6(int,int)),adder[6],SLOT(add int, int)));
    connect(this,SIGNAL(myAdd7(int,int)),adder[7],SLOT(add int, int)));
    

    So... Whats my problem? Its easy to connect the singal DoneAdding with the slot AddResult (8 different senders, 1 same slot)
    But what im not sure is how to connect the "Starting Signals".
    On your example

    connect(this, SIGNAL(myAdd(int, int)), adder, SLOT(add(int, int)));
    

    you are connecting a Signal to a specific thread, it doesnt leave room for adding more threads.

    I have experience with QSignalMapper, but this class doesnt work exactly for what i want. According to the docs

    "The QSignalMapper class bundles signals from identifiable senders.

    This class collects a set of parameterless signals, and re-emits them with integer, string or widget parameters corresponding to the object that sent the signal."

    From what I understand, QSignalMapper works if i want to connect different Objects, to the same slot, adding a parameter.
    I have a single Object (My main program) that i want to connect it to 8 different Worker objects. Im not sure if theres a "Tidy" way to do this, or If i have to simply create 8 signals...
    On my case creating 8 signals would work. But what If i, instead of wanting 8 threads, I want a variable amount, for example, on the beggining of my program doing

    QThread::idealThreadCount();
    

    and then instancing the amount of threads according to that number... I think it would be impossible...
    Any tips?


  • Moderators

    @NicolasKsi

    connect(thread[i],SIGNAL(started()),this,SLOT(threadStarted()));
    

    In threadStarted() you can use sender() to get the pointer to the sender - in this case the pointer to the thread which sent the signal. You can use this pointer.


  • Moderators

    @NicolasKsi Well you can connect them like jsulm showed but then each click of the button will start all threads adding. Which I don't think you want to do. If that is the case, do what @jsulm said and you're all done.

    If you have these threads as a thread pool then you will want to have some sort of handler to determine load and send your work to an available thread.

    I would write a class to manage your thread pool and load balancing. Then I would call an add type function in my thread pool/load balancer and it would determine which thread to use and signal it to process accordingly. To do this you could have different signals for different threads to start. I would honestly use a different method for your threads for this scenario though. Something more traditional with a semaphore to deal with available resources and conditional events to wake up specific threads. It's all doable with QThread, but you wouldn't use the moveToThread style, instead you would use QThread as a base.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.