【问题标题】:Qt QThread fails instantly when runQt QThread在运行时立即失败
【发布时间】:2014-03-27 02:01:56
【问题描述】:

Qt 线程在运行时立即失败,这是我的标题:

class Stepper1_run : public QThread
{
public:
    void system_run();
private:
    void run();
};

这是 .cpp 代码:

void Stepper1_run::run(){
    //do whatever
}


void Stepper1_run::system_run()
{
   Stepper1_run stepper1_run;
   stepper1_run.start();
}

当从 gui 中单击按钮时调用它:

void MainWindow::on_button_run_clicked()
{
    Stepper1_run stepper1_run;
    stepper1_run.system_run();
}

最初,system_run() 函数本身只是一个函数,我被告知它需要是类的成员,否则线程将立即关闭。不幸的是,该函数在运行时仍会立即关闭,并出现错误:`QThread: Destroyed while thread is still running" 并且程序退出。

发生这种情况的原因是什么?

【问题讨论】:

标签: c++ multithreading qt qthread


【解决方案1】:

你已经在栈上声明了你的线程对象,这里:

void MainWindow::on_button_run_clicked()
{
    Stepper1_run stepper1_run;  // <- Stepper1_run is instantiated
    stepper1_run.system_run();  // <- Thread starts
}  // <- stepper1_run goes out of scope and is destroyed.

对象一旦超出范围就会被销毁,这会在您调用system_run() 后立即发生,因此会出现错误。

使其成为班级成员是一种选择。您可以将其设为指针(或 Qt 自动指针之一);如果您一次只能运行该线程的一个实例,则这是合适的。要启动线程,请实例化 Stepper1_run,启动它并存储指针。要停止线程,请发出停止信号,加入它,然后删除实例并重置指针。当包含类也被销毁时,请务必在销毁线程之前停止线程(您可以在包含类的析构函数中执行通常的停止 -> 加入 -> 删除逻辑)。

更新; cmets 中要求的示例:

class MainWindow : ... {
    ...
public:
    ~MainWindow ();
public slots:
    void startSystem ();
    void stopSystem ();
private:
    QScopedPointer<Stepper1_run> stepper_;
};

MainWindow::~MainWindow () {
    stopSystem(); // <- proper termination of thread on exit
}

void MainWindow::startSystem () {
    stopSystem();
    stepper_.reset(new Stepper1_run());
    stepper_->system_run();
}

void MainWindow::stopSystem () {
    if (stepper_) { 
        stepper_->whateverStopsTheThread(); // <- you'd need to implement this
        stepper_->wait(); // <- i.e. join; waits for thread to terminate
        stepper_.reset(); // <- i.e. delete
    }
}

该示例是众多可能性之一,您的情况可能会有所不同。为简洁起见,还省略了错误处理/异常安全(详细信息也取决于您的情况)。为简单起见,上面的示例假设只有一个线程应该在运行,并且启动一个新线程应该终止旧线程。

顺便提一下,通过使用slots,您可以简单地将按钮的connect() 信号clicked() 发送到startSystem()。这也为您提供了一种从其他线程调用startSystem()stopSystem() 并让Qt 处理同步细节的方法。

如果您打算将指向线程的指针传递给其他对象,请使用 QSharedPointer(它的 API 与 QScopedPointer 略有不同,因此请阅读文档)。如果出于某种原因需要,请使用 C 样式的指针,它类似于上述情况中的 QScopedPointer,如果需要,请记住在 MainWindow 构造函数中将其初始化为 NULL

【讨论】:

  • 你能告诉我你想到的代码是什么样的吗?这个解释对我来说都很有意义,尽管 OOP 我是个菜鸟,但我一直在努力让指针正常工作,但没有成功。
  • 在您的类 MAinWindow 中添加:Stepper1_run *_stepper1_run 作为成员。您也可以使 Stepper1_run::system_run() 方法成为连接到按钮的插槽。或者你可以重载 QThread 的起始槽
  • @BenREGNIER 好建议,尤其是关于:插槽。次要点:带有前导下划线的标识符由 C++ 标准保留,供 C++ 实现使用; stackoverflow.com/questions/228783/… 有很好的描述。从技术上讲,限制仅在全局命名空间中,但通常最好是安全的;你永远不知道什么时候你可能会与例如冲突。库标头中名称不佳的宏。
【解决方案2】:

只需像这样在标头上声明 stepper1_run 即可。

class Stepper1_run : public QThread
      {
      public:
      void system_run();
      private:
     void run();
     Stepper1_run stepper1_run;
      };


    void MainWindow::on_button_run_clicked()
    {
     stepper1_run.system_run();
    }  

【讨论】:

  • 错误:标题中的“字段'stepper1_run'的类型不完整”
  • 一个结构/类不能将自己作为一个成员(它将是无限递归的);虽然它可以有一个指向自身作为成员的指针。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-31
  • 2019-04-15
  • 2013-06-08
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多