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

multithreading signal slot mecanisem



  • hi,

    I read so mach about signal-slot and the useage , but a very big qustion is still without an שnswer.

    The main quation - is signal slot is thread safe when 2 signals emit at the same time ?

    example - in the example i added 2 source example of thread , i excluded the UI thread, its not importent for the qustion.

    1. controler
    2. Udp reader
    3. UI thread

    **As you see i the main process here that i recive data from udp , process it and then send it to the UI.
    All the connections is Auto connection.

    The way i do it,

    1. connect controler and UDP threads with signal-slot - when the udp is ready, it add it to the que
    2. controler activate with timer time out intervals, get data from the que to temp list and then handle the data.

    The qustion, should i add mutex on the que or it thread safe (according to the signal slot not according to QQueueimplemantion) - there are to signals that attend to reach to the data ,
    the timer signal event and the udp signal event.

    i hope i gave enough :) and that my qustion is clear.

    I can solve my problem with double que and mutex but i want to understand if i actualy need it
    **

    //Thread A

    #ifndef MAINCONTROLER_H
    #define MAINCONTROLER_H
    
    #include <QObject>
    #include <QThread>
    
    #include "projectDefs.h"
    #include "adataparsehandler.h"
    #include "mainworkerthread.h"
    #include <QObject>
    #include <QQueue>
    #include <QTimer>
    
    
    class mainControler : public QObject
    {
        Q_OBJECT
    public:
        explicit mainControler(QObject *parent = nullptr);
        ~mainControler();
    
    public slots:
        void addRcvedDataToQue(QByteArray datagram);
        void processNewUdpData();
        void initUdpWorkerThread(QString ip, int port);
        void initData();
    
    signals:
        void updateUI(SDataStructure);
    
    private:
        IDataParseHandler *m_handler;
        mainWorkerThread *m_mainWorkerThread1;
        QQueue<QByteArray> m_rcvedDataQueue;
    
        QTimer* timer;
    
    };
    
    #endif // MAINCONTROLER_H
    
    
    #include "maincontroler.h"
    
    mainControler::mainControler(QObject *parent) : QObject(parent)
    {
        m_handler = new ADataParseHandler() ;
    }
    
    void mainControler::initData()
    {
        timer = new QTimer(this);
        connect(timer, SIGNAL(timeout()), this, SLOT(processNewUdpData()));
        timer->start(200);
    }
    
    mainControler::~mainControler()
    {
        delete  m_mainWorkerThread1;
        delete m_handler;
        delete timer ;
    }
    
    
    void mainControler::initUdpWorkerThread(QString ip, int port)
    {
        m_mainWorkerThread1 = new mainWorkerThread(ip,port);
    
        QThread* thread = new QThread;
        m_mainWorkerThread1->moveToThread(thread);
        QObject::connect(thread, SIGNAL(started()), m_mainWorkerThread1, SLOT(initUdpSocket()));
        thread->start();
        connect(m_mainWorkerThread1, SIGNAL(newUdpDataRecived(QByteArray)), this, SLOT(addRcvedDataToQue(QByteArray)));
    }
    
    void mainControler::addRcvedDataToQue(QByteArray datagram)
    {
        m_rcvedDataQueue.enqueue(datagram);
    }
    
    #include <QList>
    void mainControler::processNewUdpData()
    {
        static int m_counter = 0 ;
    
        if(!m_rcvedDataQueue.isEmpty())
        {
            QList<QByteArray> tempList;
            while(!m_rcvedDataQueue.isEmpty())
                tempList.append(m_rcvedDataQueue.dequeue());
    
            QByteArray datagram;
            while (!tempList.isEmpty())
            {
                m_counter++;
                datagram = tempList.takeFirst();
    
                SBuffData dataRcved = m_handler->parse(datagram);
    
    // HERE SHOULD BE SOME PROCESSING 
    
                static int framecounter = 0;
                if(dataRcved.header.framecounter - framecounter > 1)
                {
                    qDebug() << "diff " << QString::number(dataRcved.header.framecounter - framecounter)<<"\n";
                }
                framecounter = dataRcved.header.framecounter;
                emit updateUI(data);
            }
        }
    }
    
    

    // Thread B

    #ifndef MAINWORKERTHREAD1_H
    #define MAINWORKERTHREAD1_H
    
    #include <QObject>
    #include "projectDefs.h"
    #include <QUdpSocket>
    
    class mainWorkerThread : public QObject
    {
        Q_OBJECT
    public:
        explicit mainWorkerThread(QString ip, int port,QObject *parent = nullptr);
        ~mainWorkerThread();
    
    signals:
       void newUdpDataRecived(QByteArray);
    
    private slots:
        void processRcvData();
    public slots:
        void initUdpSocket();
    
    private:
    
        // udp DATA
        QUdpSocket *m_socket;
        int m_counter;
    
        QString m_ip;
        int m_port;
    };
    
    #endif // MAINWORKERTHREAD1_H
    
    
    #include "mainworkerthread.h"
    #include <QDataStream>
    
    #include "interfaceTest.h"
    
    mainWorkerThread::mainWorkerThread(QString ip, int port, QObject *parent) : QObject(parent),
        m_counter(0)
    {
        m_ip = ip;
        m_port = port;
    }
    
    mainWorkerThread::~mainWorkerThread()
    {
        delete m_socket;
    }
    
    void  mainWorkerThread::initUdpSocket()
    {
        m_socket = new QUdpSocket();
        bool socetInitRetVal = m_socket->bind(QHostAddress(m_ip), m_port);
    
        qDebug() << "socet init" << socetInitRetVal;
    
        connect(m_socket,SIGNAL(readyRead()),this,SLOT(processRcvData()));
    }
    
    void mainWorkerThread::processRcvData()
    {
        m_counter++;
    
        QByteArray datagram;
        QHostAddress sender;
        quint16 senderPort;
    
        datagram.resize(m_socket->pendingDatagramSize());
        m_socket->readDatagram(datagram.data(),datagram.size(),&sender,&senderPort);
    
        SBuffData* m = reinterpret_cast<SBuffData*>(datagram.data());
        static int framecounter = 0;
        if(m->header.framecounter - framecounter > 1)
        {
            qDebug() << "diff " << QString::number(m->header.framecounter - framecounter)<<"\n";
        }
        framecounter = m->header.framecounter;
        emit newUdpDataRecived(datagram);
    }
    
    


  • @Lior said in multithreading signal slot mecanisem:

    The main quation - is signal slot is thread safe when 2 signals emit at the same time ?

    Yes. The slot executions are serialized as long as the slot is only executed by a signal being sent, because it is the Qt event loop that dispatches the call to the slot, and the event loop is single threaded (unless I'm drastically mistaken). If you manually call the slot method from multiple threads then all bets are off.


  • Lifetime Qt Champion

    Hi,

    The fact that you are accessing one resource from multiple threads like that implies that you have to properly lock these accesses.
    Qt signals and slots does not mean that you do not have to properly lock your queue. The slot will be called sequentially as the queued connection is implemented in such a way but you are in a producer/consumer design with all the implications it has.


Log in to reply