Question

I have a class, which is an abstraction of some device.

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

signals:
  void sMsgArrived();
}  

Start() and Stop() are called from GUI thread. Start() begins new thread, which runs MsgLoop(). It looks like this:

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

When Stop() is called, program should return from MsgLoop() and stop the thread. How can I implement this with QThread without subclassing it?

Was it helpful?

Solution

Generally you have to decide who will be responsible for managing the thread. Is it the Device or the main window? Or possibly some device manager. In your case the Device should probably manage its own thread, so if you don't want to subclass it, use composition:

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();
}

NOTE: the thread stopping will only work if ReadMsg really is non-blocking. If you later decide to switch to blocking read (and that would probably be appropriate for most cases), you will have to figure out another way how to stop your thread.

OTHER TIPS

If you look at this link you can see that it is possible to run a method in a separate thread without subclassing a QThread.

However what you are asking is running a message loop forever.

If you follow the given example you can run your loop without subclassing but the QThread object will never enter into its own message loop cause it will never return from your slot. So here is an example but I think it would be a bad design

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);

}

I am afraid you have to subclass a QThread if you want to run a forever loop.

IMHO you shouldn't. Polling requires being in a forever loop. You must do this in QThread's run function so there is no way to re-implement a function without sub-classing first. Even if you were to try and workaround it with a single shot timer I don't recommend it. You are better off(this is how i like to do it) sub-classing QThread, calling moveToThread(), not call exec() and put a forever loop in run. For an example of this look at the Fortune Blocking Client example from qt. If you don't call moveToThread() on QThread then the QThread object still resides in the GUI main thread and they both share the same event loop (which is bad when using polling functions). Calling moveToThread(QThread) without calling exec() means the QThread will not have an event loop(which is good in your case). Calling exec() would start it's own event loop but is not used for polling schemes and you would leave the run function.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top