我已经完成了一个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在一起,因此我可以使用互斥锁正确锁定它).到目前为止一切顺利,它对我有用......但对我来说似乎很恶心!切换指向布尔语的指针听起来像......对我不好的编程......
问题是网络上的文档主要(完全是?)关于生产者和消费者线程,它们在有限的时间后完成,而不是在被要求时完成.没有开发中断线程,这就是为什么我问:有更好的方法吗?
我知道切换标志而不是调用某种专门的方法看起来很丑,但在现实生活中它是最好的方法.专门的方法通常非常危险,例如参见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)
这听起来是一个好方法.您还可以看看它是如何使其提升线程的.因为它使用异常来中止线程,所以它允许你在几个地方使用中断线程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)