2011-06-16 76 views
3

我有一个类,它是一些设备的抽象。是否可以使用QThread实现轮询而不进行子类化?

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

signals: 
    void sMsgArrived(); 
} 

启动()和stop()从GUI线程调用。 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 
     } 
    } 
} 

当停止()被调用,程序应该从MsgLoop(返回),并停止线程。我怎样才能实现这与QThread没有继承它?

回答

4

通常你必须决定谁将负责管理线程。它是设备还是主窗口?或者可能有一些设备管理器在你的情况下,设备可能应该管理自己的线程,所以如果你不想继承它,用组成:

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真的是非阻塞线程停止只会工作。如果您后来决定切换到阻止读取(这可能适用于大多数情况),您将不得不找出另一种方式来阻止您的线程。

+0

看起来不错,我会试试这个。另外,你的笔记给了我一个想法。我将使用ReadMsg()的阻塞版本。 Inside Stop()我将重置设备(这是可以接受的),所以ReadMsg()应该返回错误代码。因此,我会知道现在是退出循环的时候了。 – 2011-06-16 09:14:01

+0

听起来不错。通常在繁忙的等待循环中有一个线程是不是一个好主意 - 除非你无法承受操作系统调度程序的延迟,但我猜这不是你的情况:) – Fiktik 2011-06-16 09:33:29

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的,如果你想运行一个永远循环。

+1

'thread-> exit(0)'will will在你的例子中不起作用(它将阻止GUI),因为线程正在自己的循环中运行,并且永远不会进入事件循环。 – Fiktik 2011-06-16 08:31:40

+0

然而,使用事件循环重写'MsgLoop'来调用它自己会很容易,并且这将适用于您的方法:) – Fiktik 2011-06-16 08:33:34

+0

实际上,在启动信号发出之前,QThread将创建一个内部调度程序,但尚未启动事件循环。它希望调度程序在启动信号返回后启动。如果在MsgLoop插槽中启动事件循环,QThread不知道这一点,并且在MsgLopp终止后,QThread将尝试执行其默认运行方法,该方法将再次启动事件循环!所以在这一点上,我不知道会发生什么。无论如何,正如我所说的那样,即使你描述的是作品而不是这种复杂的作品,但它最好能够继承QThread。更简单的是不是? – 2011-06-16 08:51:47

0

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

相关问题