Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The simple answer: you're not supposed to block in asynchronous, run-to-completion code -- every event handler and slot implementation in a <code>QObject</code> is supposed to do its job and return, as soon as possible. It's not supposed to do any sort of busy waiting or sleeping. For more ranting along this line, see Miro Samek's <a href="http://embeddedgurus.com/state-space/2010/04/i-hate-rtoses/" rel="nofollow noreferrer">I hate RTOSes</a>.</p> <p><strong>For a much better implementation that follows from the above, see <a href="https://stackoverflow.com/a/32595398/1329652">this answer instead</a>.</strong> Macro trickery that follows below is best left to the poor souls stuck with C.</p> <p>I've attached an example of how to do it the right way at least from the point of view of what the code does. If you want a real implementation, look no farther than <a href="http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/overview/core/coroutine.html" rel="nofollow noreferrer">Boost's stackless coroutines</a>.</p> <p>The <em>macro trickery is syntactic sugar</em> - it makes the technique more palatable (Boost does it better than I do below). Whether you use macros or write out the methods explicitly, is up to you. The syntax is <em>not</em> what is claimed to be the "right way" of doing it. I'm <a href="http://blog.think-async.com/2009/08/secret-sauce-revealed.html" rel="nofollow noreferrer">not the only one to use such preprocessor trickery</a>. Missing is support nested function calls, and multiple "threads" of run-to-completion execution within a <code>QObject</code>. The example shows code for only one "thread" and only one level of async function calls. <a href="http://ftp.python.org/workshops/2000-01/proceedings/papers/tismers/spcpaper.pdf" rel="nofollow noreferrer">Stackless Python</a> takes this to the logical conclusion.</p> <p>You'll see this pattern in all of your code if you write it in an asynchronous way. The <code>SLEEP</code> macro is syntax sugar to help make the code easier to follow. There's no truly clean way to write it without a hacky macro in C++ where the syntax wouldn't be overbearing. Even as of C++11, the language has no built-in support for yield. See <a href="https://stackoverflow.com/questions/3864410/why-wasnt-yield-added-to-c0x">Why wasn&#39;t yield added to C++0x?</a>.</p> <p>This is truly non-blocking code, you'll see that the periodic timer event fires while you're "asleep". Do note that this cooperative multitasking has a much lower overhead than thread/process switches done by the OS. There's a reason why 16 bit Windows application code was written this way: it performs quite well, even on meager hardware.</p> <p>Note that this code does <em>not</em> need a <code>QThread</code>, and in fact doesn't use a <code>QThread</code>, although if you'd move the object to a high priority thread, the delays will have lower spread.</p> <p>The Qt timer implementation is clever enough to decrease the timer tick period on Windows, if the period is "short". You can use the platform-specific code I show below, but it should be discouraged. On Qt 5, you'd simply start a <code>Qt::PreciseTimer</code> timer. Do note that on pre-Windows 8 systems you're trading off power consumption and a slightly higher kernel overhead for performance here. Windows 8, OS X (xnu) and modern Linux are tickless and don't suffer from such performance degradation.</p> <p>I should acknowledge the clear preprocessor abuse direction from <a href="https://stackoverflow.com/questions/1597007/creating-c-macro-with-and-line-token-concatenation-with-positioning-macr">Creating C macro with ## and __LINE__ (token concatenation with positioning macro)</a>.</p> <p>Similarly to the <code>SLEEP()</code> macro, you can also implement a <code>GOTO()</code> macro, to allow you having simple finite state machines that are written in an easier-to-follow blocking code style, yet are asynchronous behind the scenes. You can have <code>ENTER()</code> and <code>LEAVE()</code> macros to implement actions to be done on state entry and exit, etc, yet the code can look entirely like a straight-coded blocking-style function. I've found it quite productive, and easier to follow than code that lacks any syntactic sugarcoating. YMMV. In the end, you would have something that's on the way to UML statecharts, but with less overhead (both runtime and code-text-wise) than <code>QStateMachine-based</code> implementations.</p> <p>Below is the output, the asterisks are periodic timer ticks.</p> <pre><code>doing something * * * * * * * * * * slept, a=10 * * * * * slept, a=20 * * slept, a=30 * slept, a=40 </code></pre> <pre><code>#sleep.pro QT += core QT -= gui TARGET = sleep CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp </code></pre> <pre><code>//main.cpp #ifdef Q_WS_WIN #include &lt;windows.h&gt; #endif #include &lt;cstdio&gt; #include &lt;QtCore/QTextStream&gt; #include &lt;QtCore/QObject&gt; #include &lt;QtCore/QBasicTimer&gt; #include &lt;QtCore/QTimer&gt; #include &lt;QtCore/QCoreApplication&gt; QTextStream out(stdout); // this order is important #define TOKENPASTE2(x,y) x ## y #define TOKENPASTE(x,y) TOKENPASTE2(x,y) #define SLEEP(ms) sleep(ms, &amp;SLEEPCLASS::TOKENPASTE(fun, __LINE__)); } void TOKENPASTE(fun, __LINE__)() { class Object : public QObject { Q_OBJECT #define SLEEPCLASS Object // used by the SLEEP macro public: Object() { QTimer::singleShot(0, this, SLOT(slot1())); periodic.start(100); connect(&amp;periodic, SIGNAL(timeout()), SLOT(tick())); } protected slots: void slot1() { a = 10; // use member variables, not locals out &lt;&lt; "doing something" &lt;&lt; endl; sleep(1000, &amp;Object::fun1); } void tick() { out &lt;&lt; "*" &lt;&lt; endl; } protected: void fun1() { out &lt;&lt; "slept, a=" &lt;&lt; a &lt;&lt; endl; a = 20; SLEEP(500); out &lt;&lt; "slept, a=" &lt;&lt; a &lt;&lt; endl; a = 30; SLEEP(250); out &lt;&lt; "slept, a=" &lt;&lt; a &lt;&lt; endl; a = 40; SLEEP(100); out &lt;&lt; "slept, a=" &lt;&lt; a &lt;&lt; endl; qApp-&gt;exit(); } private: int a; // used in place of automatic variables private: void sleep(int ms, void (Object::*target)()) { next = target; timer.start(ms, this); } void timerEvent(QTimerEvent * ev) { if (ev-&gt;timerId() == timer.timerId()) { timer.stop(); (this-&gt;*next)(); } } QTimer periodic; QBasicTimer timer; void (Object::* next)(); }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Object o1; #ifdef Q_WS_WIN timeBeginPeriod(1); // timers will be accurate to 1ms #endif return a.exec(); } #include "main.moc" </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload