Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly interrupt a QThread infinite loop

Tags:

c++

qt

I'm done implementing a Go program where the human can interrupt at any time the software to order it to play. Basically, I've got an algorithm running in another thread that has at every moment a "Best move" available, that it keeps on improving.

My question is : how do I correctly interrupt a thread in an infinite loop?

I've tried a few things, and resolved to do this :

class MyWorker : public QObject
{
    Q_OBJECT
public:
    MyWorker();
    ~MyWorker();

public:
    ThreadControl * getThreadControl();

public slots:
    void work();

private:
    void endOfComputation();

private:
    ThreadControl * threadControl;
}

Notice that I don't subclass QThread:

class ThreadControl : public QObject
{
    Q_OBJECT
public:
    ThreadControl();

public:
    bool getAbort();
    Parameter getParameter();
    void setParameter(Parameter & param);

public slots:
    void setAbort(bool b);

private:
    QMutex mutex;
    bool abort;
    Parameter param;
};

And, finally, the infinite loop is coded as this :

void Myworker::work()
{
    // ...
    forever
    {
        abort = threadControl->getAbort();
        if(abort)
        {
            break;
        }
        // ...
    }
    endOfComputation();
}

Then, as you can guess, in the main, I regularly call ThreadControl::setAbort(true)

Basically, I just keep a pointer to a boolean in the main thread and I toggle it when I want to. (The boolean is encapsulated in ThreadControl so I can properly lock it with a mutex). So far so good, it has worked for me... But it seems disgusting to me! Toggling pointers to booleans sound like ... bad programming to me...

The problem is the documentation on the web is mostly (entirely?) about producers and consumers threads, which finish after a finite time, not when asked to. Interrupting a thread is not developped, that's why I'm asking : Is there a better way?

like image 353
B. Decoster Avatar asked Feb 04 '11 12:02

B. Decoster


2 Answers

I understand that toggling flags instead of calling some sort of specialized methods looks ugly, but in real life it's the best way to do it. Specialized methods are usually very dangerous, see QThread::terminate() for example. Some environments provide ready-to-use flags so you don't have to add your own booleans, like Java with its Thread.interrupt() and Thread.interrupted(). Qt has no such thing, and maybe that's good too because interrupting sometimes works kind of counter-intuitively in Java. Take the difference between Thread.interrupted() and Thread.isInterrupted() for example. It is absolutely counter-intuitive. Unless you consult the docs, you can hardly guess what's the difference. Worse, since one of them is static, you may think that's the difference, but it's not. Also, old-style IO operations can't be interrupted in Java, but new-style NIO can, which makes no sense either.

Sometimes you can avoid flags by using something else. For example, if a thread has some sort of input queue of elements to process, you could use a special end-of-queue element to indicate that it should stop right there. But sometimes there is no convenient place to put this, that's when you use boolean flags.

The only thing you could do to optimize your code a bit is to replace mutex locking with a volatile bool. This will not guarantee memory access ordering, though, so if your thread depends on something that happens around volatile write, you shouldn't go this way. Or you could use a QAtomicInt instead with its memory barriers. But if there is no significant performance impact, using a mutex is fine, and safest too.

I'd also replace the loop with:

while (!threadControl->getAbort()) {
  // ...
like image 125
Sergei Tachenov Avatar answered Nov 12 '22 04:11

Sergei Tachenov


That sounds a good way to do it. You could also give a look at how it has been made it boost thread. Because it uses exceptions to abort the thread, it permits you to interrupt the thread in several places using interruption_point. Using their thread model you could write your thread function like this:

void myFunction(){
    boost::this_thread::interruption_point();
}

void Myworker::work()
{
    // ...
    try 
    {
        forever
        {
            boost::this_thread::interruption_point();
            // do some work
            boost::this_thread::interruption_point();
            // work again
            myFunction(); // interruption might be triggered inside this function
        }
        endOfComputation();
    }
    catch(boost::thread_interrupted const &){
         // The thread has been interrupted
    }
}

I think that internally, they use a boolean (per thread) which is set to true when boost::thread::interrupt() method is called.

EDIT

My goal is to show you how boost resolved this problem. Of course, this will not work with your QThread. I don't want you to switch to boost::thread either.

EDIT2 Quick implementation with QThread:

void function();

class MyWorker : public QThread {
public:

    MyWorker() : m_isInterrupted(false) {}

    class InterruptionException {
    public:
        InterruptionException(){}
    };
    static void interruptionPoint(){
        MyWorker * myWorker = dynamic_cast<MyWorker*>(QThread::currentThread());
        if(myWorker){
            if(myWorker->m_isInterrupted){
                throw InterruptionException();
            }
        }
    }

public slots:
    void interrupt(){
        m_isInterrupted = true;
    }
    void work(){
        try {
            while(true){
                MyWorker::interruptionPoint();
                function();
            }
        }
        catch(InterruptionException const &){

        }
    }

private:
    bool m_isInterrupted;
};

void function(){
    MyWorker::interruptionPoint();
}
like image 33
tibur Avatar answered Nov 12 '22 04:11

tibur