如何使用QThreadPool取消线程

时间:2014-03-21 02:17:02

标签: multithreading qt thread-safety

我无法共享代码,但下面是一个例子。这是我的问题。想象一下,我有一个控制器类,它有一个QThreadPool实例。此类保留工作对象列表。当我启动一个runnable时,我创建一个worker并将它交给runnable。 runnable在不同的线程上操作每个worker。当runnable完成后,它会将信号发送回控制器对象。然后,控制器对象从worker中读取结果并删除worker。控制器保留所有工作程序的列表,以便它可以在工作程序上设置取消标志,以便线程可以在必要时提前退出。

问题是QThreadPool会在它被销毁时等待所有线程完成,这将在控制器析构函数中。由于信号在从线程触发时排队,因此可能取消控制器析构函数中的工作人员将对要传递给控制器​​的信号进行排队。但由于控制器将位于析构函数的上下文中,因此这些信号不会碰到控制器插槽,因此工作对象不会被破坏。

也许我没有以应有的方式处理线程池。如果我销毁runnable中的worker对象,那么在另一个线程试图取消一个worker之前,有可能销毁该worker。任何人都可以推荐一种创建可取消线程池的好方法吗?

class Controller : public QObject {
     Q_OBJECT
public:
     virtual ~Controller();
     QThreadPool pool;
     void startTask();
     Q_SLOT void onWorkerDone(Worker * worker);
     QList<Worker*> workers;
     void cancelAll();
}
void Controller::startTask() {
     Worker * worker = new Worker();
     connect(pool, SIGNAL(done(Worker *)), this, SLOT(onWorkerDone(Worker *));
     workers << worker;
     pool.start(new Runnable(worker);
}
void Controller::onWorkerDone(Worker * worker) {
    if ( worker ) {
        // read worker...
        delete worker;
    }
}
Controller::~Controller() {
     cancelAll();
     // Destructor now kills pool object.
     // The pool will wait until all threads complete.
     // If there are any workers still running on the
     // other threads, their done() signals will be 
     // queued to this instance, and since we're in 
     // the dtor, they won't make it to the slot, 
     // and worker won't get deleted.
}
void Controller::cancelAll() {
    // loop the list and call cancel.
}

2 个答案:

答案 0 :(得分:1)

  1. 第一个错误。您不应该创建QThreadPool。这是应用程序的对象,因此您应该使用QThreadPool::globalInstance()
  2. 如果您想取消工作,您必须在Worker课程中提供一些功能。你需要在Worker::run()方法中使用一些循环,它将完成你的工作并汇集价值,这应该在循环完成时安全地改变。

答案 1 :(得分:0)

简单的解决方案是子类QThreadPool并向该类添加aboutToWait信号。信号需要在析构函数中发出。然后,您可以将信号连接到控制器的cancelAll()插槽。

要使其工作,必须在工人列表之后将声明为池

假设您销毁控制器(this)。发生以下情况:

  1. this.~Controller的正文执行。

    最后,该对象仍然是Controller的实例。现在必须将其拆分为基类的实例。成员将按照声明的顺序进行破坏。

  2. 首先销毁
  3. threadPoolthreadPool.~MyThreadPool发出aboutToWait

  4. this.cancelAll运行,因为它直接连接。该调用源于moc生成的信号方法的实现。它让工人知道他们应该停下来。

  5. threadPool.~MyThreadPool运行的余数,以及成员(如果有)以与声明相反的顺序被破坏。 this现在是QThreadPool

  6. 的一个实例
  7. threadPool.~QThreadPool跑,等待工人停下来。当每个工作人员停止时,controller.onWorkerDone被调用,因为它直接连接到相关的线程池信号。最终,threadPool.~QObject运行池完全被破坏。

  8. workers.~QList运行。

  9. this.~QObject运行。

  10. 控制器完全被破坏。