是否可以使用QThread实现轮询而不进行子类化?

时间:2011-06-16 07:28:59

标签: c++ multithreading qt qthread

我有一个类,它是某个设备的抽象。

class Device  
{  
public:  
  ...  
  void Start();  
  void Stop();  
  void MsgLoop();

signals:
  void sMsgArrived();
}  

从GUI线程调用Start()和Stop()。 Start()开始运行MsgLoop()的新线程。它看起来像这样:

void MsgLoop()  
{
   forever {  
      if(SUCCESS == ReadMsg()) //synchronous, non-blocking
      {
        ProcessMsg(); //quite fast
        emit sMsgArrived(); //this signal is connected with a slot in GUI thread  
      }
   }
}

当调用Stop()时,程序应该从MsgLoop()返回并停止该线程。如何在没有子类化的情况下使用QThread实现它?

3 个答案:

答案 0 :(得分:5)

通常,您必须决定由谁来负责管理线程。它是设备还是主窗口?或者可能是一些设备管理员在您的情况下,设备应该可以管理自己的线程,因此如果您不想将其子类化,请使用组合:

class Device : QObject
{
    Q_OBJECT
public:
    Device(QObject * parent = NULL);
    void Start();  
    void Stop();  

private slots:
    void MsgLoop();

signals:
    void sMsgArrived();

private:
    QThread thread;
    bool stopThread;
};


Device::Device(QObject * parent) : QObject(parent)
{
    moveToThread(&thread);
    connect(&thread, SIGNAL(started()), this, SLOT(MsgLoop()));
}

void Device::Start()
{
    stopThread = false;
    thread.start();
}

void Device::Stop()
{
    stopThread = true;
    thread.wait();      // if you want synchronous stop
}

void Device::MsgLoop()
{
  // your loop
  while(!stopThread)
    if(SUCCESS == ReadMsg())
    {
      ProcessMsg();
      emit sMsgArrived();
    }
  QThread::currentThread->quit();
}

注意:只有当ReadMsg非阻塞时,线程停止才有效。如果你以后决定切换到阻塞读取(这可能适用于大多数情况),你将不得不找到另一种方法来阻止你的线程。

答案 1 :(得分:2)

如果查看此link,您可以看到可以在单独的线程中运行方法而无需继承QThread。

然而,您所要求的是运行消息循环永远

如果您按照给定的示例操作,则可以在不进行子类化的情况下运行循环,但QThread对象永远不会进入自己的消息循环,因为它永远不会从您的插槽返回。所以这是一个例子,但我认为这将是糟糕设计

class Device : public QObject
{
Q_OBJECT

public:
Device(QObject* parent = 0);
~Device();

public Q_SLOTS:
  void MsgLoop();

};

QThread* thread = new QThread;
Device* device = new Device;

void Widget::onBtnStartClicked()
{

    device->moveToThread(thread);

    //This will call start method of Device
    connect(thread, SIGNAL(started()), device, SLOT(MsgLoop()));

    //This will start the event loop of thread
    thread->start();
}

void Widget::onBtnStopClicked()
{
 //Tells the thread to exit
 thread->exit(0);

}

如果您想要运行永远循环,我恐怕您必须继承QThread。

答案 2 :(得分:0)

恕我直言,你不应该。轮询需要永远循环。你必须在QThread的run函数中执行此操作,这样就无法在没有先进行子类化的情况下重新实现函数。即使您尝试使用单次计时器进行解决,我也不推荐它。你最好(我喜欢这样做)对QThread进行子类化,调用moveToThread(),而不是调用exec()并在运行中放置一个永久循环。有关此示例,请查看qt中的Fortune Blocking Client示例。如果你没有在QThread上调用moveToThread(),那么QThread对象仍然驻留在GUI主线程中,并且它们都共享相同的事件循环(使用轮询函数时这是不好的)。在不调用exec()的情况下调用moveToThread(QThread)意味着QThread不会有事件循环(在您的情况下这很好)。调用exec()会启动它自己的事件循环但不用于轮询方案,你会离开run函数。