如何正确中断QThread无限循环

时间:2011-02-04 12:23:17

标签: c++ qt

我已经完成了一个Go程序,人类可以在任何时候打断软件来命令它播放。基本上,我有一个算法在另一个线程中运行,每个时刻都有一个“最佳移动”可用,它会不断改进。

我的问题是:如何在无限循环中正确中断线程?

我尝试了一些事情,并决定这样做:

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

public:
    ThreadControl * getThreadControl();

public slots:
    void work();

private:
    void endOfComputation();

private:
    ThreadControl * threadControl;
}

请注意我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;
};

最后,无限循环被编码为:

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

然后,正如您所猜测的那样,在main,我经常致电ThreadControl::setAbort(true)

基本上,我只是在主线程中保留一个指向布尔值的指针,并在我想要时切换它。 (布尔值封装在ThreadControl中,因此我可以使用互斥锁正确锁定它。到目前为止一切顺利,它对我有用......但对我来说似乎很恶心!切换指向布尔语的指针听起来像......给我编程错误......

问题是网络上的文档主要(完全是?)关于生产者和消费者线程,它们在有限的时间后完成,而不是在被要求时完成。没有开发中断线程,这就是为什么我要问:有更好的方法吗?

2 个答案:

答案 0 :(得分:7)

我知道切换标志而不是调用某种专门的方法看起来很难看,但在现实生活中它是最好的方法。专门的方法通常非常危险,例如QThread::terminate()。有些环境提供了现成的标志,因此您不必添加自己的布尔值,例如带有Thread.interrupt()和Thread.interrupted()的Java。 Qt没有这样的东西,也许这也很好,因为在Java中,中断有时会违反直觉。以Thread.interrupted()Thread.isInterrupted()之间的差异为例。这绝对是违反直觉的。除非您查阅文档,否则很难猜出有什么区别。更糟糕的是,由于其中一个是静态的,你可能认为那是的区别,但事实并非如此。此外,旧式IO操作不能在Java中断,但新式NIO可以,这也没有意义。

有时您可以通过使用其他内容来避免使用标记。例如,如果某个线程具有某种要处理的元素的输入队列,则可以使用特殊的end-of-queue元素来指示它应该在那里停止。但有时候没有方便的地方放置它,那就是当你使用布尔标志时。

您可以做的唯一优化代码的方法是用挥发性bool替换互斥锁定。但是,这并不能保证内存访问顺序,所以如果你的线程依赖于volatile写入时发生的事情,你就不应该这样做。或者您可以使用QAtomicInt代替其内存障碍。但是,如果没有显着的性能影响,使用互斥锁也很好,也是最安全的。

我还用以下代码替换循环:

while (!threadControl->getAbort()) {
  // ...

答案 1 :(得分:5)

听起来这是一个很好的方法。您还可以查看它是如何成为boost thread的。因为它使用异常来中止线程,所以它允许你使用interruption_point在几个地方中断线程。使用他们的线程模型,您可以像这样编写线程函数:

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
    }
}

我认为在内部,他们使用布尔值(每个线程),在调用boost::thread::interrupt()方法时设置为true。

修改

我的目标是向您展示增强如何解决此问题。当然,这不适用于您的QThread。我不希望你切换到boost :: thread。

EDIT2 使用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();
}