Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Permanent QThread
Qt 6.11 is out! See what's new in the release blog

Permanent QThread

Scheduled Pinned Locked Moved Solved General and Desktop
22 Posts 6 Posters 5.1k Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • A ambershark

    @DavidPL I believe you can find thread information in the newer task managers (win 10+ probably). I can't be 100% sure though since I'm really not a big windows user.

    You can run resource monitor and it should have that type of information too.

    And finally, the easiest way is just in a debugger.

    Subclassing QThread sounds like something you're looking for in this example. Like @SGaist said above it isn't necessarily bad, it depends on what you want to do. If you are concerned with the event loop dealing with your QTimer then you are doing enough in your thread that you probably want to subclass. Keep in mind doing that much in your thread without sleep/yield is going to eat up a cpu. Most people will not run software that eats up a cpu while it is supposed to be idle.

    If you give us more details of what your thread is doing we can give you a good way to handle it, or some advice if it isn't something you should be doing. :)

    D Offline
    D Offline
    DavidPL
    wrote on last edited by
    #9

    @ambershark Ok i'll try to explain briefly and clearly what the thread is supposed to do. I'm developing a ground station. In the main window you see a interactive satellite map, state variables of the aircraft like velocity, altitude, etc, and communications options between the ground station and the aircraft. As I don't want to overload the main window and get it frozen, the thread i was talking about is meant to take care of the data transferred between the ground station and the vehicle. The communication is serial and the ground station can receive and send data. I want to check for new upcoming data or send data as fast as I can and for this reason I think about implementing the serial communications in a dedicated thread.

    Thank you so much for all the help :).

    A 1 Reply Last reply
    0
    • D DavidPL

      @ambershark Ok i'll try to explain briefly and clearly what the thread is supposed to do. I'm developing a ground station. In the main window you see a interactive satellite map, state variables of the aircraft like velocity, altitude, etc, and communications options between the ground station and the aircraft. As I don't want to overload the main window and get it frozen, the thread i was talking about is meant to take care of the data transferred between the ground station and the vehicle. The communication is serial and the ground station can receive and send data. I want to check for new upcoming data or send data as fast as I can and for this reason I think about implementing the serial communications in a dedicated thread.

      Thank you so much for all the help :).

      A Offline
      A Offline
      ambershark
      wrote on last edited by
      #10

      @DavidPL Ok so from the sounds of it you don't really need a tight loop in your thread.

      If you are using something derived from QIODevice like a QSerialPort for communications then you can just put the QIODevice::readyRead signal on the thread you created with the non-subclassing method showed in that link above.

      Then when you want to write, you just send a message to that threaded QObject with the data you want to write.

      No timers or loops needed. I mean there is an indirect loop, the QEventLoop, but that is why it's easier to not subclass. Since you then do not have to deal with your own QEventLoop, it is handled for you.

      My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

      D 2 Replies Last reply
      2
      • A ambershark

        @DavidPL Ok so from the sounds of it you don't really need a tight loop in your thread.

        If you are using something derived from QIODevice like a QSerialPort for communications then you can just put the QIODevice::readyRead signal on the thread you created with the non-subclassing method showed in that link above.

        Then when you want to write, you just send a message to that threaded QObject with the data you want to write.

        No timers or loops needed. I mean there is an indirect loop, the QEventLoop, but that is why it's easier to not subclass. Since you then do not have to deal with your own QEventLoop, it is handled for you.

        D Offline
        D Offline
        DavidPL
        wrote on last edited by
        #11

        @ambershark Sorry it took me so long to answer. I'll try what you've metioned previously.
        Thanks :)

        1 Reply Last reply
        0
        • A ambershark

          @DavidPL Ok so from the sounds of it you don't really need a tight loop in your thread.

          If you are using something derived from QIODevice like a QSerialPort for communications then you can just put the QIODevice::readyRead signal on the thread you created with the non-subclassing method showed in that link above.

          Then when you want to write, you just send a message to that threaded QObject with the data you want to write.

          No timers or loops needed. I mean there is an indirect loop, the QEventLoop, but that is why it's easier to not subclass. Since you then do not have to deal with your own QEventLoop, it is handled for you.

          D Offline
          D Offline
          DavidPL
          wrote on last edited by
          #12

          @ambershark I'm back. I've implemented what we discussed here and it works very well. The only thing that is annoying me is that I'm getting a warning in the debbuger console saying:

          "QObject: Cannot create children for a parent that is in a different thread.
          (Parent is QSerialPort(0x39905ad8), parent's thread is QThread(0x35c99f60), current thread is QThread(0x399047e8) kernel\qobject.cpp: 771"

          I don't understand why is that happening. Also I want to mention that none of the tread's addresses displayed in the debugger correspond with the upper ones.

          jsulmJ 1 Reply Last reply
          0
          • mranger90M Offline
            mranger90M Offline
            mranger90
            wrote on last edited by
            #13

            Actually, that warning is not benign. You are creating an object whose parent belongs to a different thread. Depending on how things time out, you could get a mysterious crash during, say, deleteLater() if the threads shut down in a particular order.

            1 Reply Last reply
            2
            • D DavidPL

              @ambershark I'm back. I've implemented what we discussed here and it works very well. The only thing that is annoying me is that I'm getting a warning in the debbuger console saying:

              "QObject: Cannot create children for a parent that is in a different thread.
              (Parent is QSerialPort(0x39905ad8), parent's thread is QThread(0x35c99f60), current thread is QThread(0x399047e8) kernel\qobject.cpp: 771"

              I don't understand why is that happening. Also I want to mention that none of the tread's addresses displayed in the debugger correspond with the upper ones.

              jsulmJ Offline
              jsulmJ Offline
              jsulm
              Lifetime Qt Champion
              wrote on last edited by
              #14

              @DavidPL Please show your code. In your thread object you should create instances of your variables AFTER moving thread object to the thread.

              https://forum.qt.io/topic/113070/qt-code-of-conduct

              D 1 Reply Last reply
              1
              • A Offline
                A Offline
                ambershark
                wrote on last edited by
                #15

                Chances are you just need to not parent an object you are passing to your thread. I.e. if you say auto *x = new QObject(this) instead just use auto *x = new QObject(). That way you don't have a parent that is in say your main thread and a child (the object you created) that was moved to another thread. You will then have to deal with your memory for x though as it won't have a parent to clean it up. If you don't delete it when you're done it will leak.

                As @mranger90 mentioned though, do not ignore that warning, it is most definitely not benign.

                My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

                1 Reply Last reply
                0
                • jsulmJ jsulm

                  @DavidPL Please show your code. In your thread object you should create instances of your variables AFTER moving thread object to the thread.

                  D Offline
                  D Offline
                  DavidPL
                  wrote on last edited by
                  #16

                  @jsulm This is the code of mainwindow.cpp/.h and the object.cpp/.h.
                  mainwindow.h:

                  #ifndef MAINWINDOW_H
                  #define MAINWINDOW_H
                  
                  #include <QMainWindow>
                  
                  #include "serialcomm.h"
                  #include <QSerialPortInfo>
                  #include <QThread>
                  
                  namespace Ui {
                  class MainWindow;
                  }
                  
                  class MainWindow : public QMainWindow
                  {
                      Q_OBJECT
                  
                  public:
                      explicit MainWindow(QWidget *parent = 0);
                      ~MainWindow();
                  
                  signals:
                      void setup_serialport(const QString, int);
                      void close_serialport();
                  
                  public slots:
                      void commtype_changed();
                      void showresponse(const QString &s);
                  
                  private slots:
                      void on_pushButton_open_port_clicked();
                  
                  private:
                      Ui::MainWindow *ui;
                      QStringList comm_types;
                      QThread *thread_serial = new QThread;
                      SerialComm *serialcomm = new SerialComm;
                  };
                  
                  #endif // MAINWINDOW_H
                  
                  

                  mainwindow.cpp:

                  #include "mainwindow.h"
                  #include "ui_mainwindow.h"
                  
                  MainWindow::MainWindow(QWidget *parent) :
                      QMainWindow(parent),
                      ui(new Ui::MainWindow)
                  {
                      ui->setupUi(this);
                  
                      ui->map->setSource(QUrl(QStringLiteral("qrc:/qml/mapbox.qml")));
                  
                      // Set items for communications types:
                      comm_types << "Wire" << "Bluetooth" << "Wifi" << "Radio";
                      ui->comboBox_comm_type->addItems(comm_types);
                  
                      // Get info about available serial ports in the specified communication type:
                      const auto serial_obj = QSerialPortInfo::availablePorts();
                      switch(comm_types.indexOf(ui->comboBox_comm_type->currentText())){
                      case 0:
                          break;
                      case 1:
                          for(const QSerialPortInfo &serialports_info : serial_obj){
                              if(serialports_info.description().indexOf("Bluetooth") != -1){
                                  ui->comboBox_serialports->addItem(serialports_info.portName());
                              }
                          }
                          break;
                      case 2:
                          break;
                      case 3:
                          break;
                      }
                  
                      // Default baudrate:
                      ui->lineEdit_baudrate->setText("115200");
                  
                      // Connect all the GUI elements with the corresponding SLOTS:
                      //connect(thread_serial, SIGNAL(started()), serialcomm, SLOT());
                      connect(this, SIGNAL(setup_serialport(const QString, int)), serialcomm, SLOT(setup_serial(const QString, int)));
                      connect(this, SIGNAL(close_serialport()), serialcomm, SLOT(stop_serial()));
                      connect(serialcomm, SIGNAL(response(QString)), this, SLOT(showresponse(QString)));
                      connect(ui->comboBox_comm_type, SIGNAL(activated(QString)), this, SLOT(commtype_changed()));
                      connect(serialcomm, SIGNAL(finished()), thread_serial, SLOT(quit()));
                      //connect(serialcomm, SIGNAL(finished()), serialcomm, SLOT(deleteLater()));
                      //connect(thread_serial, SIGNAL(finished()), thread_serial, SLOT(deleteLater()));
                  
                      // Move the serial communications to a dedicated thread (assign a thread to serial communications in order to avoid the GUI from freezing):
                      serialcomm->moveToThread(thread_serial);
                  }
                  
                  MainWindow::~MainWindow()
                  {
                      delete ui;
                  }
                  
                  void MainWindow::commtype_changed(){
                      // Update info about available serial ports in the specified communication type:
                      const auto serial_obj = QSerialPortInfo::availablePorts();
                      switch(comm_types.indexOf(ui->comboBox_comm_type->currentText())){
                      case 0:
                          ui->comboBox_serialports->clear();
                          for(const QSerialPortInfo &serialports_info : serial_obj){
                              ui->comboBox_serialports->addItem(serialports_info.portName());
                          }
                          break;
                      case 1:
                          ui->comboBox_serialports->clear();
                          for(const QSerialPortInfo &serialports_info : serial_obj){
                              if(serialports_info.description().indexOf("Bluetooth") != -1){
                                  ui->comboBox_serialports->addItem(serialports_info.portName());
                              }
                          }
                          break;
                      case 2:
                          break;
                      case 3:
                          break;
                      }
                  }
                  
                  void MainWindow::showresponse(const QString &s){
                      ui->label_edit_last_packreceived->setText(s);
                  }
                  
                  void MainWindow::on_pushButton_open_port_clicked()
                  {
                      // Clicking connect initializes the serial comms:
                      if(QString::compare(ui->pushButton_open_port->text(), "Connect", Qt::CaseInsensitive) == 0){
                          // Reconfiguration with new selected parameters:
                          thread_serial->start();
                          emit this->setup_serialport(ui->comboBox_serialports->currentText(), ui->lineEdit_baudrate->text().toInt());
                  
                          ui->pushButton_open_port->setText("Close");
                          ui->bar_comm_status->setValue(100);
                      } else{
                          ui->pushButton_open_port->setText("Connect");
                          ui->bar_comm_status->setValue(0);
                          emit close_serialport();
                      }
                  }
                  
                  

                  serialcomm.h (object created to read serial data):

                  #ifndef SERIALCOMM_H
                  #define SERIALCOMM_H
                  
                  #include <QObject>
                  #include <QThread>
                  #include <QMutex>
                  #include <QSerialPort>
                  #include <QIODevice>
                  
                  class SerialComm : public QObject
                  {
                      Q_OBJECT
                  public:
                      explicit SerialComm(QObject *parent = nullptr);
                  
                  signals:
                      void response(const QString &s);
                      void finished();
                  
                  public slots:
                      void setup_serial(const QString &portName, int baudrate);
                      void run_serial();
                      void stop_serial();
                  
                  private:
                      QSerialPort serial;
                      QString serial_portname;
                      int serial_baudrate;
                      int serial_timeout;
                      QMutex serial_mutex;
                      bool serial_stop = false;
                  };
                  
                  #endif // SERIALCOMM_H
                  

                  serialcomm.cpp:

                  #include "serialcomm.h"
                  
                  SerialComm::SerialComm(QObject *parent) : QObject(parent)
                  {
                  
                  }
                  
                  void SerialComm::setup_serial(const QString &portName, int baudrate)
                  {
                      // Serial port is configured and opened with the selected parameters:
                      // Values selected in the GUI are passed:
                      serial_portname = portName;
                      serial_baudrate = baudrate;
                      serial_timeout = 3000;
                  
                      // Reset stop condition:
                      serial_stop = false;
                  
                      serial.setPortName(serial_portname);
                      serial.setBaudRate(baudrate);
                  
                      // Open selected port:
                      serial.open(QIODevice::ReadWrite);
                      connect(&serial, SIGNAL(readyRead()), this, SLOT(run_serial()));
                  
                      // The rest of parameters are the default ones (no parity, 8 databits, 1 stopbit).
                  }
                  
                  void SerialComm::run_serial()
                  {
                      //if(!serial_stop) {
                      if (!serial.isOpen()) { // Try to open the selected port in Read/write mode.
                          //emit error(tr("Can't open %1, error code %2")
                          //.arg(m_portName).arg(serial.error()));
                          return;
                      }
                  
                      //if (serial.waitForReadyRead(serial_timeout)) {
                          QByteArray responseData = serial.readAll();
                          //            while (serial.waitForReadyRead(10))
                          //                responseData += serial.readAll();
                  
                          const QString response = QString::fromUtf8(responseData);
                          emit this->response(response);
                      //}
                      //}
                  }
                  
                  void SerialComm::stop_serial()
                  {
                      disconnect(&serial, SIGNAL(readyRead()), this, SLOT(run_serial()));
                      serial.close();
                      emit this->finished();
                      //serial_stop = true;
                  }
                  
                  1 Reply Last reply
                  0
                  • A Offline
                    A Offline
                    ambershark
                    wrote on last edited by ambershark
                    #17

                    Ok so the problem here is because you have a QSerialPort in your class definition (on the stack). This means that it's memory is in the main thread and not the thread you created for your SerialComm object.

                    Since you create the SerialComm object in your main thread it's memory is allocated in that thread and not in your target thread.

                    Once you moveToThread on your SerialComm object and it tried to use SerialComm::serial that creates that issue because the memory for it is not in the same thread.

                    The solution here is to allocate your memory after your thread is started inside a function called in the new thread. So you can connect a signal for thread start to an init type function in your SerialComm class. Then in there setup your serial variable. You need to change the definition to a pointer instead of on the stack and then just new it inside the thread.

                    That should take care of that warning.

                    My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

                    D 1 Reply Last reply
                    3
                    • A ambershark

                      Ok so the problem here is because you have a QSerialPort in your class definition (on the stack). This means that it's memory is in the main thread and not the thread you created for your SerialComm object.

                      Since you create the SerialComm object in your main thread it's memory is allocated in that thread and not in your target thread.

                      Once you moveToThread on your SerialComm object and it tried to use SerialComm::serial that creates that issue because the memory for it is not in the same thread.

                      The solution here is to allocate your memory after your thread is started inside a function called in the new thread. So you can connect a signal for thread start to an init type function in your SerialComm class. Then in there setup your serial variable. You need to change the definition to a pointer instead of on the stack and then just new it inside the thread.

                      That should take care of that warning.

                      D Offline
                      D Offline
                      DavidPL
                      wrote on last edited by
                      #18

                      @ambershark Ok now it works perfectly :). I thought that the movetoThread function pass all the variables and objects to the new thread. Only for curiosity, is it correct then that I have variables declared on the stack (like serial_baudrate or serial_portname) and use them on the new thread??.

                      A 1 Reply Last reply
                      0
                      • D DavidPL

                        @ambershark Ok now it works perfectly :). I thought that the movetoThread function pass all the variables and objects to the new thread. Only for curiosity, is it correct then that I have variables declared on the stack (like serial_baudrate or serial_portname) and use them on the new thread??.

                        A Offline
                        A Offline
                        ambershark
                        wrote on last edited by ambershark
                        #19

                        @DavidPL Sure you can do that. That problem is with things derived from QObject. Those have to be allocated on the same thread where they are referenced.

                        All your other stuff you can allocate the memory (stack or heap) in whatever thread you want.

                        Edit: do keep in mind if you write to memory in multiple threads you will crash. Those bugs are hard to find too. Make sure if you write to a piece of memory in multiple threads that any access is protected via a QMutex or some other synchronization object.

                        My L-GPL'd C++ Logger github.com/ambershark-mike/sharklog

                        1 Reply Last reply
                        1
                        • SGaistS Offline
                          SGaistS Offline
                          SGaist
                          Lifetime Qt Champion
                          wrote on last edited by
                          #20

                          One small adjustment: moveToThread will move the object and all its children so if you give your QSerialPort a parent at construction time (so allocate it on the heap), it should be properly moved when you move SerialComm.

                          Interested in AI ? www.idiap.ch
                          Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                          D 1 Reply Last reply
                          4
                          • SGaistS SGaist

                            One small adjustment: moveToThread will move the object and all its children so if you give your QSerialPort a parent at construction time (so allocate it on the heap), it should be properly moved when you move SerialComm.

                            D Offline
                            D Offline
                            DavidPL
                            wrote on last edited by
                            #21

                            @SGaist Oh ok, that's interesting to know. Thanks :).

                            1 Reply Last reply
                            0
                            • D Offline
                              D Offline
                              DavidPL
                              wrote on last edited by
                              #22

                              Thank you very much guys, your help was truly useful ;).

                              1 Reply Last reply
                              0

                              • Login

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • Users
                              • Groups
                              • Search
                              • Get Qt Extensions
                              • Unsolved