How to kill a QThread



  • Hello,

    I am using a QThread to do a parallel task.
    To do this :
    -I have created an instance of my "ObjectInThread" class that I moved into a QThread.
    -I connected the thread started signal to a slot of my ObjectInThread class
    -Then I started the QThread
    This works fine and the function in my "ObjectInThread" executes correctly.

    However I cannot find a way to interrupt it. All I have seen on forums is to write the code in my "ObjectInThread" as a while loop that check if it should go on or not, the condition being change by the main thread. In my case the code of "ObjectInThread" cannot be written like this and is more like a long function that I would like to interrupt at any time.

    Do you know if there a way to kill a QThread instantenously like it could be done for a process ? I tried to use terminate and used QThread::setTerminationEnable() but it does not work either.

    Thanks for your help,
    François


  • Moderators

    show some code ... especially with terminate() and setTerminationEnable().
    Nobody can tell you what you are doing wrong just by the sentence "it doesn't work" ...


  • Moderators

    Before continuing, know this: Terminating a thread at any time can leave your program in an unstable state -- If the thread is writing data when it is terminated, the data will become garbage; if the thread is holding on to any resources (e.g. opened a file) when it is terminated, the resources will not be released.

    Are you certain that it's safe to terminate the thread at any arbitrary time? If so, post your code like raven-worx said, and we'll try to help you.



  • If your code is more like a long linear flow, then the best you can do is insert checks for such a flag at regular intervals in your code. Or consider if it really is a problem that your thread runs its course to the end, even if you don't need the result value any more.

    Make sure that if you use a flag, that you can set it from outside your thread itself, and that the variable is declared volatile. Otherwise, it might be optimized out... An alternative is to use a flag of type that is inherently synced, such as [[doc:QAtomicInt]].

    Using terminate on a thread really is very last resort, that should be avoided if at all possible.



  • Thank you for your replies.
    My apologize for the late answer but I have internet acess only early in the morning and in the end of the day.

    First the code as demanded by raven-worx : Here you will find a simplify code made rapidedly with a thead I cannot terminate.

    mainobject.cpp
    @
    #include "mainobject.h"
    #include <QDebug>

    MainObject::MainObject()
    {
    m_thread = new ThreadEx();
    m_object = NULL;
    }

    void MainObject::start()
    {
    if(m_object)
    delete m_object;

    m_object = new ObjectInThread();
    m_object->moveToThread(m_thread);
    QObject::connect(m_thread,SIGNAL(started()),m_object,SLOT(run()));
    m_thread->start();
    

    }

    void MainObject::stop()
    {
    qDebug()<<"I try to stop\n";
    m_thread->terminate();
    }

    @

    mainobject.h
    @
    #ifndef MAINOBJECT_H
    #define MAINOBJECT_H

    #include "ThreadEx.h"
    #include "ObjectInThread.h"

    class MainObject : public QObject
    {
    Q_OBJECT

    public:
    MainObject();

    public slots:
    void start();
    void stop();
    private:
    ThreadEx * m_thread;
    ObjectInThread * m_object;

    };

    #endif // MAINOBJECT_H
    @

    ObjectInThread.cpp
    @
    #include "ObjectInThread.h"
    #include <QDebug>
    #include "Windows.h"
    ObjectInThread::ObjectInThread(QObject *parent) :
    QObject(parent)
    {
    }

    void ObjectInThread::run()
    {
    int compteur = 0;
    //here i am using a loop instead of the long linear flow but it is not a loop in the real code
    while(1)
    {
    qDebug() <<compteur;
    compteur++;
    Sleep(1000);
    }
    }
    @

    ObjectInThread.h
    @
    #ifndef OBJECTINTHREAD_H
    #define OBJECTINTHREAD_H

    #include <QObject>

    class ObjectInThread : public QObject
    {
    Q_OBJECT
    public:
    explicit ObjectInThread(QObject *parent = 0);

    signals:

    public slots:
    void run();
    };

    #endif // OBJECTINTHREAD_H
    @

    ThreadEx.cpp
    @
    ThreadEx::ThreadEx(QObject *parent) :
    QThread(parent)
    {
    }

    void ThreadEx::run()
    {
    QThread::setTerminationEnabled(true);
    exec();
    }
    @

    ThreadEx.h
    @
    #ifndef THREADEX_H
    #define THREADEX_H

    #include <QThread>

    class ThreadEx : public QThread
    {
    Q_OBJECT
    public:
    explicit ThreadEx(QObject *parent = 0);

    protected:
    void run();

    signals:

    public slots:

    };

    #endif // THREADEX_H
    @

    mainwindow.cpp
    @
    #include "mainwindow.h"
    #include "ui_mainwindow.h"

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);
    object = new MainObject();
    connect(ui->pushButton,SIGNAL(clicked()),object,SLOT(stop()));
    connect(ui->pushButton_2,SIGNAL(clicked()),object,SLOT(start()));

    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }
    @

    mainwindow.h
    @
    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include <QMainWindow>
    #include "mainobject.h"

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow
    {
    Q_OBJECT

    public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    private:
    Ui::MainWindow *ui;
    MainObject * object;
    };

    #endif // MAINWINDOW_H

    @
    mainwindow.ui
    @
    <?xml version="1.0" encoding="UTF-8"?>
    <ui version="4.0">
    <class>MainWindow</class>
    <widget class="QMainWindow" name="MainWindow">
    <property name="geometry">
    <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>300</height>
    </rect>
    </property>
    <property name="windowTitle">
    <string>MainWindow</string>
    </property>
    <widget class="QWidget" name="centralWidget">
    <widget class="QPushButton" name="pushButton">
    <property name="geometry">
    <rect>
    <x>150</x>
    <y>100</y>
    <width>93</width>
    <height>28</height>
    </rect>
    </property>
    <property name="text">
    <string>Stop</string>
    </property>
    </widget>
    <widget class="QPushButton" name="pushButton_2">
    <property name="geometry">
    <rect>
    <x>150</x>
    <y>50</y>
    <width>93</width>
    <height>28</height>
    </rect>
    </property>
    <property name="text">
    <string>Start</string>
    </property>
    </widget>
    </widget>
    <widget class="QMenuBar" name="menuBar">
    <property name="geometry">
    <rect>
    <x>0</x>
    <y>0</y>
    <width>400</width>
    <height>31</height>
    </rect>
    </property>
    </widget>
    <widget class="QToolBar" name="mainToolBar">
    <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
    </attribute>
    <attribute name="toolBarBreak">
    <bool>false</bool>
    </attribute>
    </widget>
    <widget class="QStatusBar" name="statusBar"/>
    </widget>
    <layoutdefault spacing="6" margin="11"/>
    <resources/>
    <connections/>
    </ui>
    @

    JKSH : I know that terminating a thread is not safe. The code I am working on was using a fork to create a new process to do the parallel task. I have been ask to make it with thread. To terminate it, a kill signal was used. Do you think that killing a process is a better way to finish the task ?

    Andre: Checking a flag in the ObjectInThreadCode is a good idea although I will have to be able to check it from many different classes. I already have a kind of "mutex" variable that I use to communicate with the thread. If this is the best way I will do that.

    However, I would like to understand why the previous code does not work ?

    A last question, when my thread is done, is the "ObjectInThread" deleted ?

    Thanks again.
    François


  • Moderators

    [quote]Do you think that killing a process is a better way to finish the task ?[/quote]Nope :D It's good that you're using a gentler way. It is possible to be even gentler though.

    I'm not sure why your thread won't terminate -- termination is enabled by default on *nix and Windows, so you shouldn't even need to call setTerminationEnabled(). Some questions:

    • What is your OS?
    • What version of Qt are you using?
    • How long does your task run for?
    • How can you tell that the thread hasn't terminated?
    • Why would you need to check the cancellation flag from many classes? Are there many objects getting/giving data from/to your thread? (If so, event-driven programming with signal-slot communication might be simpler and cleaner than a long linear function)

    But anyway, try each of these (not at the same time) and see how it goes:

    • Use a QThread instead of a ThreadEx -- this is the worker-object approach
    • Put all the code of ObjectInThread::run inside ThreadEx::run(), but DON'T call exec(). Don't use a worker object -- this is the subclass-QThread approach. IMPORTANT: The ThreadEx itself and its member variables all live in the main thread, but any objects created inside run() live in the parallel thread
    • If still no luck, start debugging. Probe functions to see if they are actually called, e.g.:
      @
      // Put a verbose wrapper around QThread::terminate()
      void ThreadEx::terminate()
      {
      qDebug("Starting termination attempt...");
      QThread::terminate();
      qDebug("Finished termination attempt");
      }
      @

    Read of the "latest QThread documentation":http://doc-snapshot.qt-project.org/qt5-stable/qtcore/qthread.html#details to see examples of the two approaches I mentioned above. (If you're interested, read this "blog post":http://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html too to see their history)

    Also, out of curiosity,

    • What kind of task is your parallel thread doing?
    • What does your program do when the thread is terminated?

    [quote]when my thread is done, is the “ObjectInThread” deleted ?[/quote]Not automatically. You have to connect QThread::finished() to ObjectInThead::deleteLater(). However, this only works for Qt 4.8 and later.



  • jKSH : Thanks for your answer.
    To answer your questions :

    • The OS is Redhat 6.2
    • I am using Qt 5.0.0
    • The task can run from a minute to a day.
    • I think that that the thread is not over when I tried to stop it because the print trace I put in the code are still displayed after I tried to stop the thread.
    • On the last point I totally agree with you concerning the clean aspect of the code. This software has not been designed in a signal/slot communication way.

    For your curiosity ;-) The parallel task consists in commanding devices through TCP, UDP or RS232 . Each device is modelized by a class which contains several others. All the data are shared via a QSharedMemory system. Those does not represent a lot, I would say about 200 bytes for each device. When the thread starts, the GUI is disabled except a stop button. At the end the task, I call exit to finish the task thread. Then the GUI is enabled again. Then the same or another task can be performed. A new thread is then created.



  • I tried what you told.

    Using a QThread did not change anything.

    The second approach DID work on my simple example :D (which is on Windows because I do not have Linux at home). But if I understood well, I have no loop event in my thread in this case.

    If I go back to my specific use case, I created the QUdpSocket or QTcpSocket used for communication in the main thread and then I was thinking to move them into the thread. As I have no event loop, do you think that the socket will work ?


  • Moderators

    You're welcome :)

    Hmm... if I remember correctly, Qt sockets need an event loop to function properly. But, the results of your tests suggest that event loops cause threads to be un-terminatable, so there might be no quick solution for you... (you've understood correctly: If -QThread::run()- ThreadEx::run() does not call exec(), there is no event loop)

    Are you able to upgrade your Qt version to Qt 5.0.2 or even "5.1.0":http://download.qt-project.org/snapshots/qt/5.1/5.1.0-rc1/backups/ ? I know that numerous fixes have been made to QThread since Qt 5.0.0, although I don't know if any of them would solve your problem.

    I recommend asking for help at the "Qt Interest mailing list":http://lists.qt-project.org/mailman/listinfo/interest. Some of Qt's core engineers are active there -- they probably know more than we do.



  • OK. I will do tomorrow some tests on Linux with Qt 5.1.0. After that, if I still have the problem, I will post on the mailing list to know and post their answer here.

    Thanks again for your time !



  • JSKH is right that sockets need a running eventloop. There is nothing stopping you terminating a thread that has an eventloop though. That can work just fine. Also QThread::run does by default call QThread::exec(). From the docs:
    [quote]The starting point for the thread. After calling start(), the newly created thread calls this function. The default implementation simply calls exec().[/quote]
    So, in the default case, you do have an eventloop in a thread.



  • bq. There is nothing stopping you terminating a thread that has an eventloop though. That can work just fine.

    I am sure it can if the code is developped in a signal/slot approach which means it goes regurarely in the event loop. That is not the case for the code I am using.



  • No, you can use the same technique mentioned earlier, eventloop or not.


  • Moderators

    [quote author="Andre" date="1371618822"]No, you can use the same technique mentioned earlier, eventloop or not. [/quote]That's the expected behaviour, but franchouze's tests above indicates that it doesn't work as expected for him. That's why I suggested upgrading the libraries and/or talking to Qt engineers.


Log in to reply
 

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