【问题标题】:Qt: Signal/Slot not working after QObject moved to different threadQt:QObject 移动到不同线程后,信号/插槽不起作用
【发布时间】:2012-12-29 02:14:58
【问题描述】:

我正在使用 Qt5,我通过 moveToThread() 将 QObject 工作线程传递给 QThread 的实例来实现线程。我的实现看起来像这样..

Worker.h

class worker : public QObject
{
    Q_OBJECT
public:
    explicit worker(QObject *parent = 0);
    bool IsWorkRunning();
    void MoveObjectToThread();

signal:
    void SignalToObj_mainThreadGUI();

public slots:
    void do_Work();
    void StopWork();
    void StartWork();

private:
    void Sleep();
    QThread *workerthread;    
    volatile bool running,stopped;
};

Worker.cpp

 worker::worker(QObject *parent) :
    QObject(parent),stopped(false),running(false)
{
}

void worker::do_Work()
{
    running = true;
    while(!stopped)
    {
       if(running)
       {
        emit SignalToObj_mainThreadGUI();
        workerthread->msleep(20);
       }
    }
}

void worker::StopWork()
{
    running = false;
}

void worker::StartWork()
{
    running = true;
}

bool worker::IsWorkRunning()
{
    return running;
}

void MoveObjectToThread()
{
  workerthread = new QThread;
  QObject::connect(workerthread,SIGNAL(started()),this,SLOT(do_Work()));

  this->moveToThread(workerthread);

  workerthread->start();
}

MainWindow.h

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

signals:
    void Startwork_mainwindow();
    void Stopwork_mainwindow();

public slots:

private slots:
    void on_pushButton_push_to_start_clicked();

    void on_pushButton_push_to_stop_clicked();

private:

    Ui::MainWindow *ui;
    worker myWorker;
    bool work_started;

};

MainWindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),work_started(false),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QObject::connect(this,SIGNAL(Startwork_mainwindow()),&myWorker,SLOT(StartWork()));
    QObject::connect(this,SIGNAL(Stopwork_mainwindow()),&myWorker,SLOT(StopWork()));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_push_to_start_clicked()
{
    if(!work_started)
    {
      myWorker.MoveObjectToThread();
      work_started = true;
    }

    if(!myWorker.IsWorkRunning())
       emit this->Startwork_mainwindow();
}

void MainWindow::on_pushButton_push_to_stop_clicked()
{
  if(myWorker.IsWorkRunning())
       emit this->Stopwork_mainwindow();
}

不知道为什么以下两个信号/插槽对似乎不起作用

QObject::connect(this,SIGNAL(Startwork_mainwindow()),&myWorker,SLOT(StartWork()));
QObject::connect(this,SIGNAL(Stopwork_mainwindow()),&myWorker,SLOT(StopWork()));

因此,一旦 QThread 对象的started() 信号触发了do_Work() 插槽,我就无法启动或停止线程。仅供参考,我的这篇文章是我之前的帖子here described 的延续。任何见解都会有所帮助...谢谢

【问题讨论】:

  • 在进行任何信号连接之前,您是否尝试过移至线程。就像你做完之后一样?当你的线程不运行时,你的外部 while 循环不是在疯狂地旋转吗?也许它也需要睡眠来帮助调度程序。
  • 我不会使用moveToThread,而是重新实现QThread。它更容易理解,而且我认为更不容易出错。并且在主线程使用QObject::connect到另一个线程时,不要使用AutoConnect连接到另一个线程,而是使用QueuedConnection
  • 如果我使用 Qt::Directconnection 会怎样?在论坛中我读到的一些地方将 QThread 子类化不是一个好习惯……由于这种使用范式,Qt5 已经公开了许多静态私有函数
  • 您不想重新实现QThread。查看这个和链接的讨论,了解原因。 stackoverflow.com/questions/4093159/…
  • 您是否尝试过在不启动私有 QThread 实例的情况下在您尝试移动到线程的对象内执行此操作?基本上我的意思是你应该首先尝试在你的对象之外(在主窗口甚至 main.cpp 中)创建 QThread 并将你的对象实例移动到线程并在那里启动它。

标签: multithreading qt signals-slots qthread qt5


【解决方案1】:

在您的MainWindow 类定义中,尝试将worker myWorker 更改为worker * myWorker。另外,我会按照其他人的建议将线程移到worker 类之外。 MainWindow 构造函数变成这样:

MainWindow::MainWindow(QWidget *parent)
  : QMainWindow(parent)
  , work_started(false)
  , ui(new Ui::MainWindow)
  , myWorker( new worker() )
{
  // NOTE:  myWorker is created without a parent on purpose.
  // Qt won't change the thread affinity of an obj with a parent

  ui->setupUi(this);

  connect( this, SIGNAL(Startwork_mainwindow()), myWorker, SLOT(StartWork()) );
  connect( this, SIGNAL(Stopwork_mainwindow()), myWorker, SLOT(StopWork()) );

  QThread * thread = new QThread();
  // delete the worker obj whenever this obj is destroyed
  connect( this, SIGNAL(destroyed()), myWorker, SLOT(deleteLater()) );
  // stop the thread whenever the worker is destroyed
  connect( myWorker, SIGNAL(destroyed()), thread, SLOT(quit()) );
  // clean up the thread
  connect( thread, SIGNAL(finished()), thread, SLOT(deleteLater()) );
  myWorker->moveToThread( thread );
  thread->start();
}

当然,您不再需要worker::MoveObjectToThread() 方法了。此外,从MainWindow 调用方法worker::IsWorkRunning() 并不安全。这个具体的例子你可能不会遇到任何麻烦,但是当事情变得更复杂时,它肯定会让你感到痛苦。相反,添加一个workFinished() 信号或类似的东西并在MainWindow 类中监听它。

Startwork_mainwindow() 信号将开始工作。由于您没有在对connect 的调用中提供连接类型,因此当您更改线程关联(moveToThread)时,Qt 将使用QueuedConnection。基本上,myWorker 位于具有自己事件循环的线程中。使用Qt::QueuedConnection 调用槽将事件发布到该事件循环,然后将槽方法排队。它会在事件循环到达时运行。

【讨论】:

    猜你喜欢
    • 2020-04-30
    • 1970-01-01
    • 2018-03-06
    • 1970-01-01
    • 2014-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多