1

Brief of environment: I have a device on which runs an application written in qt. It has a main thread which handles Database operations (SQlite) and a separate thread for networking operations (via 3G). Mains thread Event loop is ran by QCoreApplication::exec and the other thread which handles networking operations is ran by QThread::exec. Btw, socket thread affinity is changed after it's started (Eg. moveToThread(socketThreadPtr))

Brief of problem: Main thread is busy in a loop, in order to select around 10k records from database and that loop takes about +30 seconds. In the network thread there is a 15 seconds timer which has to send a keep alive message each time expires. The problem is that the slot for timeout() signal is executed only after the loop is finished.

Solution founded until now(but not satisfying): If I call QCoreApplication::processEvents in the loop that selects the records, problem is solved, but I wonder if a solution exists instead of this workaround.

Remark: The timer, signal and slot, which gives the command to send the keep alive message is currently handled in the main thread( but the read/write happens in the network thread). Also, I moved the timer on the network thread but I got the same result as being on the main thread.

ionutCb
  • 13
  • 6
  • Since the timer is network related it should be on the network thread. Make sure the slot connected to it is in an object also on the network thread. – Kevin Krammer Dec 16 '16 at 10:35
  • The timer probably still lives in the main thread. First: Create the socket directly on the thread or set the socketdescriptor and open the socket using a QueuedConnection. Do not create the timer in the constructor and call movetothread on the object, instead call a initialize function using invokeMethod or a queued signal/slot connection. In this function create the timer using new. Otherwise the timer will actually stay in the main thread – Sebastian Lange Dec 16 '16 at 10:38
  • Hi guys! Thanks for the quick replies. Kevin, I had the timer completely moved in the network thread, with it's corresponding slot on the network thread. Btw, the timer was created on the heap. – ionutCb Dec 16 '16 at 10:58

2 Answers2

1

You have to create timer in your network thread, maybe Qtimer is a member of network thread, so the network thread will be constructed in main thread and thread affinity of its children's set to main thread, next you have moved network thread to new Qthread, but what about Qtimer? it still lives in main thread (except when you explicitly define network class as it's parent, so as you said moveToThread will affect object's children as well as Qtimer)

The Qtimer should be constructed in network thread, you can construct a new Qtimer in one of the network thread slots and connecting it to the Qthread::start signal. So by calling start method of Qthrad your slot will be executed on new thread and Qtimer will be constructed on that thread respectively.

e.jahandar
  • 1,715
  • 12
  • 30
  • Never thought about the started signal, nice thing if its a dedicated thread to that one object! – Sebastian Lange Dec 16 '16 at 10:51
  • @SebastianLange maybe describe it more? – e.jahandar Dec 16 '16 at 10:54
  • 1
    Jahandar, you know what you are? An awesome guy that lead me to the solution. Thanks a lot! So, as a conclusion, previously I constructed the QTimer this way: new QTimer(0), in the constructor. After you asked me about the affinity of QTimer, that was it. So, I created the QTimer this way: new QTimer(this), this being the parent, network thread. QTimer is still constructed in the constructor. moveToThread() changes the affinity of the objects and it's children also. The children part I was missing. Hope I was clear enough about the solution. If not, please ask. – ionutCb Dec 16 '16 at 11:38
  • Just to make things clear, you do have a class member with the parent of a foreign thread now? Afterwards you move that class object into the same thread? What happens if your thread gets deleted and the object is never moved? Or the object is deleted but not the thread? The QTimer, if not deleted explicitly in the objects destructor now still occupies memory and runs in this thread. – Sebastian Lange Dec 16 '16 at 12:35
  • QTimer is now a member, and children, of network thread. This timer is started in this thread and it's corresponding slot it's also in this network thread. Works like a charm. – ionutCb Dec 16 '16 at 12:39
  • The fix for me was just to make the network thread, the parent of the qtimer. – ionutCb Dec 16 '16 at 12:41
  • Sebastian, the timer is deleted in network thread destructor(anyway, that was not the point of the issue, so I didn't mentioned it to add complexity). However, it should never be the case since the device is designed to maintain a non-stop connection and when gets disconnected it should reconnect. It also has some power management capabilities, but network thread is never deleted. One more thing, this device is a client not a server. If the device does not send messsges, server disconnects the client. – ionutCb Dec 16 '16 at 16:11
0

Something like this should actually create the socket and the timer in your dedicated thread, given your main-thread is the server and clients should be handled in threads. Otherwise just use QTcpSocket::connectToHost() or QTcpServer:::bind()in your initialize function.

Mainthread:

auto t = new QThread();
t->start();
auto o = new MyThreadObject();
o.moveToThread(t);
o.setDescriptor(socketDesc);
QMetaObject::invokeMethod(o, "initialize", Qt::QueuedConnection);

MyThreadObject:

class MyThreadObject : public QObject
{
  Q_OBJECT
public:
  MyThreadObject(){...};
  void setDescriptor(qintptr socketdescriptor)
  {
    m_desc = socketdescriptor;
  }
public slots:
  void initialize()
  {
    m_tcpSocket = new QTcpSocket();
    m_tcpSocket->setSocketDescriptor(m_desc);
    //socket configuration
    m_timer = new QTimer();
    //timer configuration
    m_timer->start();
  }
private:
  QTcpSocket* m_tcpSocket;
  QTimer* m_timer;
  qintptr m_desc;
}
Sebastian Lange
  • 3,879
  • 1
  • 19
  • 38