3

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

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 的情况下使用它来实现它?

4

3 回答 3

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注意:线程停止只有在确实是非阻塞的情况下才会起作用。如果您稍后决定切换到阻塞读取(这可能适用于大多数情况),您将不得不找出另一种方法来停止您的线程。

于 2011-06-16T08:11:48.287 回答
2

如果您查看此链接,您可以看到可以在单独的线程中运行方法而无需继承 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 。

于 2011-06-16T08:16:01.883 回答
0

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

于 2011-08-04T22:15:27.800 回答