如何正确中断QThread无限循环

Fez*_*vez 12 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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,我不是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;
};
Run Code Online (Sandbox Code Playgroud)

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

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

那么,你可以猜到main,我经常打电话ThreadControl::setAbort(true)

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

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

Ser*_*nov 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()) {
  // ...
Run Code Online (Sandbox Code Playgroud)


tib*_*bur 5

这听起来是一个好方法.您还可以看看它是如何使其提升线程的.因为它使用异常来中止线程,所以它允许你在几个地方使用中断线程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
    }
}
Run Code Online (Sandbox Code Playgroud)

我认为在内部,它们使用布尔值(每个线程),在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();
}
Run Code Online (Sandbox Code Playgroud)