【问题标题】:How to properly interrupt a QThread infinite loop如何正确中断 QThread 无限循环
【发布时间】:2015-05-01 22:32:21
【问题描述】:

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

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

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

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 中,所以我可以用互斥锁正确锁定它)。到目前为止一切顺利,它对我有用......但这对我来说似乎很恶心!切换指向布尔值的指针听起来像......对我来说糟糕的编程......

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

【问题讨论】:

    标签: c++ qt


    【解决方案1】:

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

    有时您可以通过使用其他东西来避免标记。例如,如果一个线程有某种元素的输入队列要处理,您可以使用一个特殊的队列结束元素来指示它应该停在那里。但有时没有方便的地方放这个,那就是当你使用布尔标志时。

    您可以做的唯一一点优化代码的事情就是用 volatile bool 替换互斥锁。但是,这并不能保证内存访问顺序,所以如果你的线程依赖于 volatile write 周围发生的事情,你不应该这样做。或者你可以使用 QAtomicInt 来代替它的内存屏障。但是,如果没有显着的性能影响,使用互斥体就可以了,而且也是最安全的。

    我还将循环替换为:

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

    【讨论】:

    • 你能链接到一些 Java 的 Thread.interrupt() 和 interrupted() 的反直觉的例子吗?
    • @Josiah,想不出任何链接,但请查看我的编辑。
    • 谢谢。这是一个有用的观察!
    【解决方案2】:

    这听起来不错。您还可以看看它是如何制作的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。

    编辑

    我的目标是向您展示 boost 是如何解决这个问题的。当然,这不适用于您的 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();
    }
    

    【讨论】:

    • boost 线程不适用于 qthread。人们经常使用 qthread,因为它是一个 qt gui 应用程序。
    • 我的目标是提供另一种中断该线程的方法。添加了示例实现。
    猜你喜欢
    • 1970-01-01
    • 2012-08-13
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多