Jump to content

Synchronize-thread-code-with-QCoreApplication-main-event-loop: Difference between revisions

From Qt Wiki
Decode HTML entity names
m Synchronize thread code with QCoreApplication main event loop
 
Line 1: Line 1:
{{Cleanup | reason=Auto-imported from ExpressionEngine.}}
If you're using C++11, and you need to execute code in QApplication thread, but you're in a different thread, and you need to wait for such event to be processed (i.e. just like connect Qt::BlockingQueuedConnection does), here's a quick method!
 
If you're using C+''11, and you need to execute code in QApplication thread, but you're in a different thread, and you need to wait for such event to be processed (i.e. just like connect Qt::BlockingQueuedConnection does), here's a quick method!


Main idea comes from QCoreApplication code, adding C''+11 lambda feature.
Main idea comes from QCoreApplication code, adding C''+11 lambda feature.
Line 8: Line 6:
class SynchronizedEvent : public QEvent
class SynchronizedEvent : public QEvent
{
{
boost::function< void () > _f;
  boost::function< void () > _f;
QSemaphore *_sem;
  QSemaphore *_sem;


public:
public:
static QEvent::Type type()
  static QEvent::Type type()
{
  {
static QEvent::Type t = static_cast< QEvent::Type >(QEvent::registerEventType());
    static QEvent::Type t = static_cast< QEvent::Type >(QEvent::registerEventType());


return t;
    return t;
}
  }


explicit SynchronizedEvent(boost::function< void () > f, QSemaphore *sem) :
  explicit SynchronizedEvent(boost::function< void () > f, QSemaphore *sem) :
QEvent(type()),
    QEvent(type()),
_f(f),
    _f(f),
_sem(sem) { }
    _sem(sem) { }


~SynchronizedEvent()
  ~SynchronizedEvent()
{
  {
if (_sem)
    if (_sem)
_sem->release();
      _sem->release();
}
  }


void execute() { _f(); }
  void execute() { _f(); }
};
};


class Application : public QApplication
class Application : public QApplication
{
{
Q_OBJECT
  Q_OBJECT
 
  Qt::HANDLE _appThreadId;


Qt::HANDLE _appThreadId;
protected:
protected:
void customEvent(QEvent '''pEvent);
  void customEvent(QEvent *pEvent);


public:
public:
explicit Application(int &argc, charargv);
  explicit Application(int &iArgc, char **pArgv);


void execute(boost::function< void () > f);
  void execute(boost::function< void () > f);
};
};


Application::Application(int &iArgc, char'''*pArgv) :
Application::Application(int &iArgc, char **pArgv) :
QApplication(iArgc,pArgv),
  QApplication(iArgc,pArgv),
_appThreadId(QThread::currentThreadId())
  _appThreadId(QThread::currentThreadId())
{
{


Line 56: Line 55:
void Application::execute(boost::function< void () > f)
void Application::execute(boost::function< void () > f)
{
{
if (QThread::currentThreadId() == _appThreadId)
  if (QThread::currentThreadId() == _appThreadId)
f();
    f();
else
  else
{
  {
QSemaphore sem;
    QSemaphore sem;
QApplication::postEvent(this,new SynchronizedEvent(f,&sem));
    QApplication::postEvent(this,new SynchronizedEvent(f,&sem));
sem.acquire();
    sem.acquire();
}
  }
}
}


void Application::customEvent(QEvent '''pEvent)
void Application::customEvent(QEvent *pEvent)
{
{
if (pEvent->type() == SynchronizedEvent::type())
  if (pEvent->type() == SynchronizedEvent::type())
static_cast< SynchronizedEvent''' >(pEvent)->execute();
    static_cast< SynchronizedEvent * >(pEvent)->execute();


QApplication::customEvent(pEvent);
  QApplication::customEvent(pEvent);
}
}
</code>
</code>
Line 77: Line 76:
The magic comes from the combination of postEvent, QSemaphore and lambda functions.
The magic comes from the combination of postEvent, QSemaphore and lambda functions.


Qt documentation reports that, when you post an event, it must be allocated on heap, and Qt main event loop will destroy it. So, if you call ''execute'' method within the same Qt main loop thread, f() will be called, just like a simple function. If you call ''execute'' from a different thread, a specialized event is created, that holds our lambda, and a pointer to a QSemaphore. Then we perform ''acquire'' on such semaphore, so we wait.
Qt documentation reports that, when you post an event, it must be allocated on heap, and Qt main event loop will destroy it. So, if you call "execute" method within the same Qt main loop thread, f() will be called, just like a simple function. If you call "execute" from a different thread, a specialized event is created, that holds our lambda, and a pointer to a QSemaphore. Then we perform "acquire" on such semaphore, so we wait.


After a while, Qt main loop processes our event, calls customEvent, so it calls our lambda (hey, we're executing code in Qt main thread[[Image:|Image:]]!), and, when Qt deletes our event from heap, destructor is called, and semaphore is released. Our thread is now free to continue its execution.
After a while, Qt main loop processes our event, calls customEvent, so it calls our lambda (hey, we're executing code in Qt main thread!), and, when Qt deletes our event from heap, destructor is called, and semaphore is released. Our thread is now free to continue its execution.


If Qt main loop processes our event before ''acquire'' occurs, it will work anyway, and we won't wait.
If Qt main loop processes our event before "acquire" occurs, it will work anyway, and we won't wait.


Simple and effective, huh?
Simple and effective, huh?

Latest revision as of 20:48, 11 November 2015

If you're using C++11, and you need to execute code in QApplication thread, but you're in a different thread, and you need to wait for such event to be processed (i.e. just like connect Qt::BlockingQueuedConnection does), here's a quick method!

Main idea comes from QCoreApplication code, adding C+11 lambda feature.

class SynchronizedEvent : public QEvent
{
  boost::function< void () > _f;
  QSemaphore *_sem;

public:
  static QEvent::Type type()
  {
    static QEvent::Type t = static_cast< QEvent::Type >(QEvent::registerEventType());

    return t;
  }

  explicit SynchronizedEvent(boost::function< void () > f, QSemaphore *sem) :
    QEvent(type()),
    _f(f),
    _sem(sem) { }

  ~SynchronizedEvent()
  {
    if (_sem)
      _sem->release();
  }

  void execute() { _f(); }
};

class Application : public QApplication
{
  Q_OBJECT

  Qt::HANDLE _appThreadId;

protected:
  void customEvent(QEvent *pEvent);

public:
  explicit Application(int &iArgc, char **pArgv);

  void execute(boost::function< void () > f);
};

Application::Application(int &iArgc, char **pArgv) :
  QApplication(iArgc,pArgv),
  _appThreadId(QThread::currentThreadId())
{

}

void Application::execute(boost::function< void () > f)
{
  if (QThread::currentThreadId() == _appThreadId)
    f();
  else
  {
    QSemaphore sem;
    QApplication::postEvent(this,new SynchronizedEvent(f,&sem));
    sem.acquire();
  }
}

void Application::customEvent(QEvent *pEvent)
{
  if (pEvent->type() == SynchronizedEvent::type())
    static_cast< SynchronizedEvent * >(pEvent)->execute();

  QApplication::customEvent(pEvent);
}

The magic comes from the combination of postEvent, QSemaphore and lambda functions.

Qt documentation reports that, when you post an event, it must be allocated on heap, and Qt main event loop will destroy it. So, if you call "execute" method within the same Qt main loop thread, f() will be called, just like a simple function. If you call "execute" from a different thread, a specialized event is created, that holds our lambda, and a pointer to a QSemaphore. Then we perform "acquire" on such semaphore, so we wait.

After a while, Qt main loop processes our event, calls customEvent, so it calls our lambda (hey, we're executing code in Qt main thread!), and, when Qt deletes our event from heap, destructor is called, and semaphore is released. Our thread is now free to continue its execution.

If Qt main loop processes our event before "acquire" occurs, it will work anyway, and we won't wait.

Simple and effective, huh?

Moreover, even if code will be called in another thread, we're blocked until the event has been processed, so we can pass references to lambda and whatever we need, without worrying about concurrency in variables destruction.

Antonio Di Monaco