2013-03-07 66 views
1

我在读我应该使用worker对象,并通过moveToThread将其移动到线程,而不是直接从QThread继承。但我无法找到解决方案如何停止我的对象工人循环。例如,我有测试回路:QThread - 如何阻止工人?

void CollectionWorker::doWork() 
{ 
    for (int i = 0; i < 10; ++i) { 
     sleep(1); 
     emit ping(i); 
    } 
} 

现在我提出这个对象的线程:

worker->moveToThread(mTh); 

这是工作的罚款。但是当我调用mTh.quit()时,线程正在等待,直到doWork中的循环结束。当我直接从QThread继承,然后在每个循环中,我可以检查线程状态并在完成完成时断开循环,但不知道如何在工作对象中执行此操作。我可以在工作对象中创建一些标志并将其从主线程中切换出来吗?或者,也许我可以找到线程所有者并检查它的状态?或者,也许在开始线程之前更好,在工作对象中设置线程指针,然后检查状态?什么是最好的线程安全解决方案?

Regards

回答

2

编辑:对不起,我误解了你的问题。

有几种选择。您可以创建一些标志,并在处理循环的每次迭代之前检查标志是否已设置。或者,如果您在列表/队列中处理数据,您可能会发信号通知进程终止一个特殊的数据结束元素。

+0

是的我知道,但这不是问题。如果我的循环是“永远”的话会怎么样?我想用类似下面的方式来阻止它: for(int i = 0; i <10; ++ i)if() return; sleep(1); 发出ping(i); } – Dibo 2013-03-07 11:14:29

+1

该标志最有意义。'while(stillDoingWork && notStopped)' – Anthony 2013-03-07 11:57:05

+0

是否在主线程(例如,来自按钮clik)的线程安全中设置工作者标志?只有主线程写入此标志,工作人员才会读取 – Dibo 2013-03-07 12:06:11

0

Qt中的惯用的方式来破坏辅助线程使用信号/槽接口:

为了这个工作CollectionWorker必须从一个QObject类继承和声明Q_OBJECT宏。

4

如果有任何运行,调用线程对象的quit()或exit()将简单地结束线程的事件循环。但正如您正确指出的那样,原始问题仍然是一样的。如果工作者函数已经被事件循环执行并且是具有永久构造的长时间运行的函数呢?对quit()或exit()的调用只需等待worker函数返回即可。

除了向调用者提供公共函数外,还可以建议两种方法,这些方法会改变内部标志。

  • 给你的工人阶级一个终止信号和插槽。如下所示。

    signals: 
     void signalTermination(); 
    public slots: 
     void setTerminationFlag(); 
    private: 
     QMutex mutex; 
     bool terminationRequested; 

你的插槽看起来像什么。

void setTerminationFlag() 
{ 
    QMutexLocker locker(&mutex); 
    terminationRequested = true; 
} 

然后,您可以在永久循环的每次迭代中检查doWork函数中的变量。

mutex.lock(); 
if(terminationRequested) 
{ 
    //break from loop and effectively doWork function 
} 
mutex.unlock(); 

对使用​​,而不是普通的成员函数的信号和槽的原因是,如果你的工人函数是做同步的代码块中需要长时间运行的任务,公共职能将保持封锁,直到它得到的访问同步对象。如果公共终止方法的调用线程是UI线程,这可能会产生不利影响。

  • 另一个干净和简单的方法,如果你使用的是Qt 5.2起

使用requestInterruption()的QThread的方法。此方法在线程对象中设置咨询标志,您可以在doWork()函数中通过isInterruptionRequested()函数调用来检查该标志。请参阅QThread::isInterruptionRequested文档中给出的以下代码片段。

void long_task() { 
    forever { 
     if (QThread::currentThread()->isInterruptionRequested()) { 
      return; 
     } 
    } 
} 

您也可以直接调用quit()来结束线程的事件循环。

+0

你的第一个建议似乎不适合我。虽然我们使用的是信号,但只有在长循环结束后才会执行'setTerminationFlag()'函数,就像您为成员函数所描述的那样。第二个选项'isInterruptionRequested()'会工作正常,如果不是因为你以后想做类似的工作而不能重置这个标志。 – 2016-08-17 08:42:16

0

我有和Dibo一样的问题。我在我的doWork()函数中运行了一个很长的计算循环,并且需要能够从我的主线程中停止它。

查迪克罗伯特的答案没有为我做的伎俩。第一个建议的表现与他为成员函数描述的一样。也就是说,虽然我们正在使用信号和插槽,但是从主线程中发出的信号调用setTerminationFlag插槽,并正确连接到它,只有在循环结束后才会执行。但是,也许我在执行方面做了错误。如果它确实应该工作,请让我知道,因为它似乎是交易断路器。

他的第二种替代方法是使用QThread::currentThread()->isInterruptionRequested()来告诉线程停止,如果不是因为你不能重置这个标志,而你打算重新使用线程和worker来执行一些类似的处理密集循环如果不一样的话。当然,你可以通过停止和启动线程来完成它,但是我不确定它是否不会有像清空执行队列那样的不利影响。这个问题实际上帐为bug report和蒂亚戈Macieira(Qt的主要开发者)提到那里,如果我可以引述他:

的requestInterruption功能的目的是完成线程。

这使得requestInterruption不适合作业,因为它仅在线程开始和结束时重置。

一个解决方案,我发现,这似乎不是所有的清洁对我来说,是包括QCoreApplication在工人阶级和时间打电话QCoreApplicaton::processEvents()时间,来处理Chadick Robbert的第一个建议那些排队的信号或更新工作者对线程之间共享标志变量的类感知。

因为你的循环中调用QCoreApplicaton::processEvents()可以减缓它极大地我做的是一样的东西:

for(unsigned long int i=0; i<800000000 && !*stop; i++){ 
    f += (double)i * .001; // dummy calculation 
    if(i%10000000 == 0) // Call it only from time to time 
     QCoreApplication::processEvents(); // update *stop according to main Thread 
} 

正如你所看到的,用此溶液循环只会在“千万”整数倍打破这可能不适合某些使用情况。

如果有人知道这个杀手解决方案,我很乐意听到。

+0

是的。第一种选择应该是这样工作的。请注意我的回答中的陈述“然后你可以在**每次迭代**中永久循环中检查doWork函数中的变量。” – 2016-08-18 15:08:27

+0

这里的要点是,如果我们想要优雅地完成线程,您希望线程至少完成其当前循环的执行。除非您的代码拥有消息泵,并且在执行每个语句之前检查它,否则没有更简洁的方法可以在循环的当前行中获取中断。正如你在你自己的答案中指出的,即使你的解决方案只能工作在10000000的倍数,但如果我想结束线程数12345678. – 2016-08-18 15:08:50

+0

因此,如果它是一个非常长的循环,请在步骤中创建逻辑检查点,然后检查该标志或调用函数'isInturruptionRequested()'。再次,我只是建议更干净的方式来结束这个线程。 – 2016-08-18 15:14:34