Synchronize-thread-code-with-QCoreApplication-main-event-loop

From Qt Wiki
Revision as of 10:15, 24 February 2015 by Maintenance script (talk | contribs)
Jump to navigation Jump to search

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.

<br />class SynchronizedEvent : public QEvent<br />{<br /> boost::function&amp;lt; void () &gt; _f;<br /> QSemaphore *_sem;

public:<br /> static QEvent::Type type()<br /> {<br /> static QEvent::Type t = static_cast&amp;lt; QEvent::Type &gt;(QEvent::registerEventType());

return t;<br /> }

explicit SynchronizedEvent(boost::function&amp;lt; void () &gt; f, QSemaphore *sem) :<br /> QEvent(type()),<br /> _f(f),<br /> _sem(sem) { }

~SynchronizedEvent()<br /> {<br /> if (_sem)<br /> _sem-&gt;release();<br /> }

void execute() { _f(); }<br />};

class Application : public QApplication<br />{<br /> Q_OBJECT

Qt::HANDLE _appThreadId;<br />protected:<br /> void customEvent(QEvent '''pEvent);
<br />public:<br /> explicit Application(int &amp;argc, charargv);
<br /> void execute(boost::function&amp;lt; void () &gt; f);<br />};
<br />Application::Application(int &amp;iArgc, char'''*pArgv) :<br /> QApplication(iArgc,pArgv),<br /> _appThreadId(QThread::currentThreadId())<br />{

}

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

void Application::customEvent(QEvent '''pEvent)<br />{<br /> if (pEvent-&gt;type() == SynchronizedEvent::type())<br /> static_cast&amp;lt; SynchronizedEvent''' &gt;(pEvent)-&gt;execute();

QApplication::customEvent(pEvent);<br />}<br />

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[[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.

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