2011-06-16 18 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()將啓動它自己的事件循環,但不用於輪詢方案,並且您將離開運行功能。

相關問題